Getrennte Fristen für Studiennachweis und Zahlung implementieren
Some checks failed
Code Quality / quality (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy (push) Has been cancelled

- Neue Felder: studiennachweis_faelligkeitsdatum (semesterbasiert) und zahlung_faelligkeitsdatum (vierteljährlich im Voraus)
- Studiennachweis-Fristen: Q1/Q2 → 15. März, Q3/Q4 → 15. September
- Zahlungsfälligkeiten: Q1 → 15. Dez (Vorjahr), Q2 → 15. Mär, Q3 → 15. Jun, Q4 → 15. Sep
- Auto-Freigabe: Q1 freigeben → Q2 Studiennachweis auto-freigegeben, Q3 → Q4
- Unterstützungserstellung: Verhindert Duplikate durch präzise Suche nach zahlung_faelligkeitsdatum
- Quartalserstellung: Modal-Formular funktioniert korrekt
- UI: Beide Fristen in Tabelle angezeigt, separate Überfälligkeits-Indikatoren
- Migration: Neue Felder hinzugefügt und bestehende Datensätze befüllt
This commit is contained in:
2025-12-30 20:20:33 +01:00
parent 24435660f5
commit 6c8ddbb4f0
6 changed files with 542 additions and 187 deletions

View File

@@ -466,7 +466,7 @@
<i class="fas fa-calendar-check me-2"></i>Vierteljährliche Nachweise
</h5>
<div>
<small class="text-light me-3">Frist: jeweils 15. des zweiten Quartalsmonats</small>
<small class="text-light me-3">Studiennachweis: 15. März / 15. September | Zahlung: vierteljährlich im Voraus</small>
<button type="button" class="btn btn-sm btn-light"
data-bs-toggle="modal"
data-bs-target="#addQuarterModal"
@@ -478,32 +478,76 @@
</div>
<div class="card-body">
{% if quarterly_confirmations %}
<style>
.quarterly-table {
font-size: 0.9rem;
table-layout: auto;
}
.quarterly-table th {
white-space: nowrap;
font-size: 0.85rem;
padding: 0.6rem 0.5rem;
}
.quarterly-table td {
padding: 0.6rem 0.5rem;
vertical-align: middle;
}
.quarterly-table .col-zeitraum {
width: 85px;
}
.quarterly-table .col-status {
width: 100px;
}
.quarterly-table .col-fortschritt {
width: 70px;
}
.quarterly-table .col-fristen {
width: 120px;
}
.quarterly-table .col-check {
width: 40px;
text-align: center;
padding: 0.6rem 0.3rem;
}
.quarterly-table .col-aktionen {
width: 110px;
}
.quarterly-table .progress {
height: 18px;
font-size: 0.75rem;
margin: 0;
}
.quarterly-table .badge {
font-size: 0.75rem;
padding: 0.3em 0.5em;
}
</style>
<div class="table-responsive">
<table class="table table-hover">
<table class="table table-hover table-sm quarterly-table">
<thead class="table-light">
<tr>
<th>Zeitraum</th>
<th>Status</th>
<th>Fortschritt</th>
<th>Fälligkeit</th>
<th>Studiennachweis</th>
<th>Einkommen</th>
<th>Vermögen</th>
<th>Aktionen</th>
<th class="col-zeitraum">Zeitraum</th>
<th class="col-status">Status</th>
<th class="col-fortschritt">Fortschritt</th>
<th class="col-fristen">Fristen</th>
<th class="col-check" title="Studiennachweis"><i class="fas fa-graduation-cap"></i></th>
<th class="col-check" title="Einkommen"><i class="fas fa-euro-sign"></i></th>
<th class="col-check" title="Vermögen"><i class="fas fa-piggy-bank"></i></th>
<th class="col-aktionen">Aktionen</th>
</tr>
</thead>
<tbody>
{% for nachweis in quarterly_confirmations %}
<tr {% if nachweis.is_overdue %}class="table-warning"{% endif %}>
<td>
<strong>{{ nachweis.jahr }} {{ nachweis.get_quarter_display|truncatechars:3 }}</strong>
<td class="col-zeitraum">
<strong>{{ nachweis.jahr }} Q{{ nachweis.quartal }}</strong>
{% if nachweis.is_overdue %}
<br><small class="text-danger"><i class="fas fa-exclamation-triangle"></i> Überfällig</small>
<br><small class="text-danger"><i class="fas fa-exclamation-triangle"></i></small>
{% endif %}
</td>
<td>
<td class="col-status">
{% if nachweis.status == 'offen' %}
<span class="badge bg-secondary">Ausstehend</span>
<span class="badge bg-secondary">Offen</span>
{% elif nachweis.status == 'teilweise' %}
<span class="badge bg-warning">Teilweise</span>
{% elif nachweis.status == 'eingereicht' %}
@@ -511,8 +555,8 @@
{% elif nachweis.status == 'geprueft' %}
<span class="badge bg-success">Freigegeben</span>
{% elif nachweis.status == 'auto_geprueft' %}
<span class="badge bg-success">
<i class="fas fa-magic me-1"></i>Auto-Freigabe
<span class="badge bg-success" title="Auto-Freigabe">
<i class="fas fa-magic"></i> Auto
</span>
{% elif nachweis.status == 'nachbesserung' %}
<span class="badge bg-warning">Nachbesserung</span>
@@ -520,9 +564,9 @@
<span class="badge bg-danger">Abgelehnt</span>
{% endif %}
</td>
<td>
<td class="col-fortschritt">
{% with completion=nachweis.get_completion_percentage %}
<div class="progress" style="height: 20px;">
<div class="progress" title="{{ completion }}%">
<div class="progress-bar
{% if completion == 100 %}bg-success
{% elif completion >= 70 %}bg-info
@@ -539,14 +583,33 @@
</div>
{% endwith %}
</td>
<td>
{% if nachweis.faelligkeitsdatum %}
<small>{{ nachweis.faelligkeitsdatum|date:"d.m.Y" }}</small>
{% else %}
<small class="text-muted">Nicht festgelegt</small>
{% endif %}
<td class="col-fristen">
<div class="small" style="line-height: 1.4;">
{% if nachweis.studiennachweis_faelligkeitsdatum %}
<div class="mb-1">
<i class="fas fa-graduation-cap text-primary" title="Studiennachweis"></i>
<span class="{% if nachweis.is_study_proof_overdue %}text-danger fw-bold{% endif %}">
{{ nachweis.studiennachweis_faelligkeitsdatum|date:"d.m.Y" }}
</span>
{% if nachweis.is_study_proof_overdue %}
<i class="fas fa-exclamation-triangle text-danger" title="Überfällig"></i>
{% endif %}
</div>
{% endif %}
{% if nachweis.zahlung_faelligkeitsdatum %}
<div>
<i class="fas fa-euro-sign text-success" title="Zahlung"></i>
<span class="{% if nachweis.is_payment_overdue %}text-danger fw-bold{% endif %}">
{{ nachweis.zahlung_faelligkeitsdatum|date:"d.m.Y" }}
</span>
{% if nachweis.is_payment_overdue %}
<i class="fas fa-exclamation-triangle text-danger" title="Überfällig"></i>
{% endif %}
</div>
{% endif %}
</div>
</td>
<td class="text-center">
<td class="col-check">
{% if nachweis.studiennachweis_eingereicht %}
{% if nachweis.studiennachweis_datei %}
<a href="{{ nachweis.studiennachweis_datei.url }}" target="_blank" class="text-success" title="Datei ansehen">
@@ -555,13 +618,13 @@
{% elif nachweis.studiennachweis_bemerkung %}
<i class="fas fa-comment text-info" title="Bemerkung vorhanden"></i>
{% else %}
<i class="fas fa-check text-success"></i>
<i class="fas fa-check text-success" title="Vorhanden"></i>
{% endif %}
{% else %}
<i class="fas fa-times text-muted"></i>
<i class="fas fa-times text-muted" title="Nicht vorhanden"></i>
{% endif %}
</td>
<td class="text-center">
<td class="col-check">
{% if nachweis.einkommenssituation_bestaetigt %}
{% if nachweis.einkommenssituation_datei %}
<a href="{{ nachweis.einkommenssituation_datei.url }}" target="_blank" class="text-success" title="Datei ansehen">
@@ -570,13 +633,13 @@
{% elif nachweis.einkommenssituation_text %}
<i class="fas fa-comment text-info" title="{{ nachweis.einkommenssituation_text|truncatechars:50 }}"></i>
{% else %}
<i class="fas fa-check text-success"></i>
<i class="fas fa-check text-success" title="Vorhanden"></i>
{% endif %}
{% else %}
<i class="fas fa-times text-muted"></i>
<i class="fas fa-times text-muted" title="Nicht vorhanden"></i>
{% endif %}
</td>
<td class="text-center">
<td class="col-check">
{% if nachweis.vermogenssituation_bestaetigt %}
{% if nachweis.vermogenssituation_datei %}
<a href="{{ nachweis.vermogenssituation_datei.url }}" target="_blank" class="text-success" title="Datei ansehen">
@@ -585,38 +648,38 @@
{% elif nachweis.vermogenssituation_text %}
<i class="fas fa-comment text-info" title="{{ nachweis.vermogenssituation_text|truncatechars:50 }}"></i>
{% else %}
<i class="fas fa-check text-success"></i>
<i class="fas fa-check text-success" title="Vorhanden"></i>
{% endif %}
{% else %}
<i class="fas fa-times text-muted"></i>
<i class="fas fa-times text-muted" title="Nicht vorhanden"></i>
{% endif %}
</td>
<td>
<div class="btn-group" role="group" aria-label="Aktionen">
<td class="col-aktionen">
<div class="btn-group btn-group-sm" role="group" aria-label="Aktionen">
<a href="{% url 'stiftung:quarterly_confirmation_edit' nachweis.id %}"
class="btn btn-sm btn-outline-primary"
class="btn btn-outline-primary btn-sm"
title="Bearbeiten">
<i class="fas fa-edit"></i>
</a>
{% if user.is_staff %}
{% if nachweis.status == 'eingereicht' %}
<button type="button"
class="btn btn-sm btn-outline-success"
class="btn btn-outline-success btn-sm"
onclick="approveQuarterly('{{ nachweis.id }}')"
title="Freigeben">
<i class="fas fa-check"></i>
</button>
{% elif nachweis.status == 'geprueft' %}
<button type="button"
class="btn btn-sm btn-outline-success"
class="btn btn-outline-success btn-sm"
onclick="approveQuarterly('{{ nachweis.id }}')"
title="Erneut freigeben / Unterstützung synchronisieren">
title="Erneut freigeben">
<i class="fas fa-sync"></i>
</button>
<button type="button"
class="btn btn-sm btn-outline-warning"
class="btn btn-outline-warning btn-sm"
onclick="resetQuarterly('{{ nachweis.id }}')"
title="Status zurücksetzen">
title="Zurücksetzen">
<i class="fas fa-undo"></i>
</button>
{% endif %}
@@ -630,58 +693,6 @@
</div>
<!-- Add Quarter Modal -->
<div class="modal fade" id="addQuarterModal" tabindex="-1" aria-labelledby="addQuarterModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addQuarterModalLabel">
<i class="fas fa-plus me-2"></i>Neues Quartal hinzufügen
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Schließen"></button>
</div>
<form method="post" action="{% url 'stiftung:quarterly_confirmation_create' destinataer.pk %}">
{% csrf_token %}
<div class="modal-body">
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="jahr" class="form-label">Jahr</label>
<select class="form-select" id="jahr" name="jahr" required>
{% for year in available_years %}
<option value="{{ year }}" {% if year == current_year %}selected{% endif %}>{{ year }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="quartal" class="form-label">Quartal</label>
<select class="form-select" id="quartal" name="quartal" required>
<option value="1">Q1 (Januar - März)</option>
<option value="2">Q2 (April - Juni)</option>
<option value="3">Q3 (Juli - September)</option>
<option value="4">Q4 (Oktober - Dezember)</option>
</select>
</div>
</div>
</div>
<div class="alert alert-info">
<i class="fas fa-info-circle me-2"></i>
Das Quartal wird mit dem aktuellen Datum als Stichtag erstellt.
Die Fälligkeit wird automatisch auf den 15. des zweiten Quartalsmonats gesetzt.
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
<button type="submit" class="btn btn-primary">
<i class="fas fa-plus me-2"></i>Quartal erstellen
</button>
</div>
</form>
</div>
</div>
</div>
{% else %}
<div class="text-center py-4">
@@ -715,6 +726,59 @@
</div>
</form>
<!-- Add Quarter Modal (außerhalb des edit-form) -->
<div class="modal fade" id="addQuarterModal" tabindex="-1" aria-labelledby="addQuarterModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addQuarterModalLabel">
<i class="fas fa-plus me-2"></i>Neues Quartal hinzufügen
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Schließen"></button>
</div>
<form method="post" action="{% url 'stiftung:quarterly_confirmation_create' destinataer.pk %}" id="addQuarterForm">
{% csrf_token %}
<div class="modal-body">
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="jahr" class="form-label">Jahr</label>
<select class="form-select" id="jahr" name="jahr" required>
{% for year in available_years %}
<option value="{{ year }}" {% if year == current_year %}selected{% endif %}>{{ year }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="quartal" class="form-label">Quartal</label>
<select class="form-select" id="quartal" name="quartal" required>
<option value="1">Q1 (Januar - März)</option>
<option value="2">Q2 (April - Juni)</option>
<option value="3">Q3 (Juli - September)</option>
<option value="4">Q4 (Oktober - Dezember)</option>
</select>
</div>
</div>
</div>
<div class="alert alert-info">
<i class="fas fa-info-circle me-2"></i>
Das Quartal wird mit dem aktuellen Datum als Stichtag erstellt.
Die Fälligkeit wird automatisch auf den 15. des zweiten Quartalsmonats gesetzt.
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
<button type="submit" class="btn btn-primary">
<i class="fas fa-plus me-2"></i>Quartal erstellen
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Förderungen -->
{% if foerderungen %}
<div class="card shadow mb-4">
@@ -1268,5 +1332,25 @@ function resetQuarterly(nachweisId) {
});
}
}
// Handle add quarter form submission using event delegation
document.addEventListener('DOMContentLoaded', function() {
// Use event delegation to catch form submission
document.addEventListener('submit', function(e) {
const form = e.target;
if (form && form.id === 'addQuarterForm') {
console.log('Add quarter form submitted');
console.log('Form action:', form.action);
// Form will submit normally, page will reload after redirect
// Modal will be closed automatically when page reloads
const submitBtn = form.querySelector('button[type="submit"]');
if (submitBtn) {
submitBtn.disabled = true;
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Wird erstellt...';
}
}
});
});
</script>
{% endblock %}

View File

@@ -155,8 +155,15 @@
{% endif %}">
{{ nachweis.get_status_display }}
</span>
{% if nachweis.faelligkeitsdatum %}
<br><small class="opacity-75">Fällig: {{ nachweis.faelligkeitsdatum|date:"d.m.Y" }}</small>
{% if nachweis.studiennachweis_faelligkeitsdatum or nachweis.zahlung_faelligkeitsdatum %}
<br><small class="opacity-75">
{% if nachweis.studiennachweis_faelligkeitsdatum %}
Studiennachweis: {{ nachweis.studiennachweis_faelligkeitsdatum|date:"d.m.Y" }}
{% endif %}
{% if nachweis.zahlung_faelligkeitsdatum %}
| Zahlung: {{ nachweis.zahlung_faelligkeitsdatum|date:"d.m.Y" }}
{% endif %}
</small>
{% endif %}
</div>
</div>
@@ -175,8 +182,25 @@
</div>
<div class="col-md-6">
<p class="mb-2"><strong>Zeitraum:</strong> {{ nachweis.jahr }} {{ nachweis.get_quarter_display }}</p>
{% if nachweis.faelligkeitsdatum %}
<p class="mb-0"><strong>Fälligkeitsdatum:</strong> {{ nachweis.faelligkeitsdatum|date:"d.m.Y" }}</p>
{% if nachweis.studiennachweis_faelligkeitsdatum %}
<p class="mb-1"><strong>Studiennachweis fällig:</strong>
<span class="{% if nachweis.is_study_proof_overdue %}text-danger{% endif %}">
{{ nachweis.studiennachweis_faelligkeitsdatum|date:"d.m.Y" }}
</span>
{% if nachweis.is_study_proof_overdue %}
<i class="fas fa-exclamation-triangle text-danger" title="Überfällig"></i>
{% endif %}
</p>
{% endif %}
{% if nachweis.zahlung_faelligkeitsdatum %}
<p class="mb-0"><strong>Zahlung fällig:</strong>
<span class="{% if nachweis.is_payment_overdue %}text-danger{% endif %}">
{{ nachweis.zahlung_faelligkeitsdatum|date:"d.m.Y" }}
</span>
{% if nachweis.is_payment_overdue %}
<i class="fas fa-exclamation-triangle text-danger" title="Überfällig"></i>
{% endif %}
</p>
{% endif %}
</div>
</div>
@@ -420,8 +444,25 @@
</span>
</p>
{% if nachweis.faelligkeitsdatum %}
<p class="mb-2"><strong>Fälligkeit:</strong> {{ nachweis.faelligkeitsdatum|date:"d.m.Y" }}</p>
{% if nachweis.studiennachweis_faelligkeitsdatum %}
<p class="mb-1"><strong>Studiennachweis fällig:</strong>
<span class="{% if nachweis.is_study_proof_overdue %}text-danger{% endif %}">
{{ nachweis.studiennachweis_faelligkeitsdatum|date:"d.m.Y" }}
</span>
{% if nachweis.is_study_proof_overdue %}
<i class="fas fa-exclamation-triangle text-danger" title="Überfällig"></i>
{% endif %}
</p>
{% endif %}
{% if nachweis.zahlung_faelligkeitsdatum %}
<p class="mb-2"><strong>Zahlung fällig:</strong>
<span class="{% if nachweis.is_payment_overdue %}text-danger{% endif %}">
{{ nachweis.zahlung_faelligkeitsdatum|date:"d.m.Y" }}
</span>
{% if nachweis.is_payment_overdue %}
<i class="fas fa-exclamation-triangle text-danger" title="Überfällig"></i>
{% endif %}
</p>
{% endif %}
{% if nachweis.eingereicht_am %}