Implement modular report system with 6 report types and composer UI
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy (push) Has been cancelled
Code Quality / quality (push) Has been cancelled

Refactors the Berichte section from a single hardcoded Jahresbericht into
a modular report-building system. Jahresbericht now uses PDFGenerator for
corporate identity (logo, colors, headers/footers, cover page). 8 reusable
section templates can be freely combined. 6 predefined report templates
(Jahres-, Destinatär-, Grundstücks-, Finanz-, Förder-, Pachtbericht) with
HTML preview and PDF export. New Bericht-Baukasten UI lets users compose
custom reports from individual sections.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
SysAdmin Agent
2026-03-14 20:55:31 +00:00
parent 042114b1e7
commit faeb7c1073
15 changed files with 1081 additions and 439 deletions

View File

@@ -0,0 +1,36 @@
<!-- Sektion: Jahresbilanz -->
<div class="section">
<h2>Jahresbilanz {{ jahr }}</h2>
<div class="bilanz-grid">
<div class="bilanz-card einnahmen">
<div class="value">&#8364;{{ total_einnahmen|floatformat:2 }}</div>
<div class="label">Einnahmen (Pacht)</div>
</div>
<div class="bilanz-card ausgaben">
<div class="value">&#8364;{{ total_ausgaben|floatformat:2 }}</div>
<div class="label">Ausgaben gesamt</div>
</div>
<div class="bilanz-card {% if netto >= 0 %}netto-positiv{% else %}netto-negativ{% endif %}">
<div class="value">{% if netto >= 0 %}+{% endif %}&#8364;{{ netto|floatformat:2 }}</div>
<div class="label">Nettosaldo</div>
</div>
</div>
<div class="stats-grid">
<div class="stat-card">
<div class="value">&#8364;{{ total_ausgaben_foerderung|floatformat:2 }}</div>
<div class="label">F&ouml;rderausgaben</div>
</div>
<div class="stat-card">
<div class="value">&#8364;{{ total_verwaltungskosten|floatformat:2 }}</div>
<div class="label">Verwaltungskosten</div>
</div>
<div class="stat-card">
<div class="value">&#8364;{{ pacht_vereinnahmt|floatformat:2 }}</div>
<div class="label">Pacht vereinnahmt</div>
</div>
<div class="stat-card">
<div class="value">&#8364;{{ grundsteuer_gesamt|floatformat:2 }}</div>
<div class="label">Grundsteuer</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,46 @@
<!-- Sektion: Destinat&auml;r-&Uuml;bersicht -->
<div class="section">
<h2>Destinat&auml;r-&Uuml;bersicht{% if jahr %} {{ jahr }}{% endif %}</h2>
<div class="stats-grid">
<div class="stat-card">
<div class="value">{{ destinataere_aktiv }}</div>
<div class="label">Aktive Destinat&auml;re</div>
</div>
<div class="stat-card">
<div class="value">{{ destinataere_gesamt }}</div>
<div class="label">Gesamt</div>
</div>
<div class="stat-card">
<div class="value">&#8364;{{ destinataere_total_unterstuetzung|floatformat:2 }}</div>
<div class="label">Gesamte Unterst&uuml;tzungen</div>
</div>
</div>
{% if destinataere_liste %}
<table>
<thead>
<tr>
<th>Name</th>
<th>Ort</th>
<th>Berufsgruppe</th>
<th>Aktiv</th>
<th>Unterst&uuml;tzungen</th>
<th>Betrag gesamt</th>
</tr>
</thead>
<tbody>
{% for d in destinataere_liste %}
<tr>
<td>{{ d.get_full_name }}</td>
<td>{{ d.ort|default:"-" }}</td>
<td>{{ d.get_berufsgruppe_display|default:"-" }}</td>
<td>{% if d.aktiv %}Ja{% else %}Nein{% endif %}</td>
<td>{{ d.unterstuetzung_count }}</td>
<td class="amount">&#8364;{{ d.unterstuetzung_summe|floatformat:2 }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>

View File

@@ -0,0 +1,41 @@
<!-- Sektion: F&ouml;rderungen (Legacy) -->
{% if foerderungen %}
<div class="section">
<h2>F&ouml;rderungen {{ jahr }}</h2>
<table>
<thead>
<tr>
<th>Beg&uuml;nstigter</th>
<th>Kategorie</th>
<th>Betrag</th>
<th>Status</th>
<th>Antragsdatum</th>
</tr>
</thead>
<tbody>
{% for f in foerderungen %}
<tr>
<td>
{% if f.destinataer %}{{ f.destinataer.get_full_name }}
{% elif f.person %}{{ f.person.get_full_name }}
{% else %}&ndash;{% endif %}
</td>
<td>{{ f.get_kategorie_display }}</td>
<td class="amount">&#8364;{{ f.betrag|floatformat:2 }}</td>
<td>
<span class="status-badge status-{{ f.status }}">{{ f.get_status_display }}</span>
</td>
<td>{{ f.antragsdatum|date:"d.m.Y" }}</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr style="font-weight: bold; background: #f0f7f4;">
<td colspan="2">Summe</td>
<td class="amount">&#8364;{{ total_foerderungen_legacy|floatformat:2 }}</td>
<td colspan="2"></td>
</tr>
</tfoot>
</table>
</div>
{% endif %}

View File

@@ -0,0 +1,76 @@
<!-- Sektion: Grundst&uuml;cksverwaltung -->
<div class="section">
<h2>Grundst&uuml;cksverwaltung</h2>
{% if verpachtungen %}
<h3>Aktive Verpachtungen</h3>
<table>
<thead>
<tr>
<th>L&auml;nderei</th>
<th>P&auml;chter</th>
<th>Verpachtete Fl&auml;che</th>
<th>Jahrespachtzins</th>
<th>Pachtende</th>
</tr>
</thead>
<tbody>
{% for v in verpachtungen %}
<tr>
<td>{{ v.land }}</td>
<td>{{ v.paechter.get_full_name }}</td>
<td class="amount">{{ v.verpachtete_flaeche|floatformat:0 }} qm</td>
<td class="amount">&#8364;{{ v.pachtzins_pauschal|floatformat:2 }}</td>
<td>{% if v.pachtende %}{{ v.pachtende|date:"d.m.Y" }}{% else %}unbefristet{% endif %}</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr style="font-weight: bold; background: #f0f7f4;">
<td colspan="3">Gesamtpachtzins (kalkuliert)</td>
<td class="amount">&#8364;{{ total_pachtzins|floatformat:2 }}</td>
<td></td>
</tr>
</tfoot>
</table>
{% endif %}
{% if landabrechnungen %}
<h3>Landabrechnungen {{ jahr }}</h3>
<table>
<thead>
<tr>
<th>L&auml;nderei</th>
<th>Pacht vereinnahmt</th>
<th>Umlagen</th>
<th>Grundsteuer</th>
<th>Sonstige Einnahmen</th>
</tr>
</thead>
<tbody>
{% for a in landabrechnungen %}
<tr>
<td>{{ a.land }}</td>
<td class="amount">&#8364;{{ a.pacht_vereinnahmt|floatformat:2 }}</td>
<td class="amount">&#8364;{{ a.umlagen_vereinnahmt|floatformat:2 }}</td>
<td class="amount">&#8364;{{ a.grundsteuer_betrag|floatformat:2 }}</td>
<td class="amount">&#8364;{{ a.sonstige_einnahmen|floatformat:2 }}</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr style="font-weight: bold; background: #f0f7f4;">
<td>Summe</td>
<td class="amount">&#8364;{{ pacht_vereinnahmt|floatformat:2 }}</td>
<td></td>
<td class="amount">&#8364;{{ grundsteuer_gesamt|floatformat:2 }}</td>
<td></td>
</tr>
</tfoot>
</table>
{% endif %}
{% if not verpachtungen and not landabrechnungen %}
<p style="color: #999;">Keine Verpachtungs- oder Abrechnungsdaten f&uuml;r {{ jahr }} vorhanden.</p>
{% endif %}
</div>

View File

@@ -0,0 +1,46 @@
<!-- Sektion: Konten-&Uuml;bersicht -->
<div class="section">
<h2>Konten&uuml;bersicht</h2>
<div class="stats-grid">
<div class="stat-card">
<div class="value">{{ konten_anzahl }}</div>
<div class="label">Aktive Konten</div>
</div>
<div class="stat-card">
<div class="value">&#8364;{{ konten_gesamtsaldo|floatformat:2 }}</div>
<div class="label">Gesamtsaldo</div>
</div>
</div>
{% if konten_liste %}
<table>
<thead>
<tr>
<th>Kontoname</th>
<th>Bank</th>
<th>Kontotyp</th>
<th>IBAN</th>
<th>Saldo</th>
</tr>
</thead>
<tbody>
{% for k in konten_liste %}
<tr>
<td>{{ k.kontoname }}</td>
<td>{{ k.bank_name }}</td>
<td>{{ k.get_konto_typ_display }}</td>
<td>{{ k.iban|default:"-" }}</td>
<td class="amount">&#8364;{{ k.saldo|floatformat:2 }}</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr style="font-weight: bold; background: #f0f7f4;">
<td colspan="4">Gesamtsaldo</td>
<td class="amount">&#8364;{{ konten_gesamtsaldo|floatformat:2 }}</td>
</tr>
</tfoot>
</table>
{% endif %}
</div>

View File

@@ -0,0 +1,41 @@
<!-- Sektion: Unterst&uuml;tzungszahlungen -->
{% if unterstuetzungen %}
<div class="section">
<h2>Unterst&uuml;tzungszahlungen {{ jahr }}</h2>
<p style="color: #666; margin-bottom: 12px;">
{{ unterstuetzungen.count }} Unterst&uuml;tzung(en) geplant/ausgezahlt &middot;
{{ unterstuetzungen_ausgezahlt.count }} &uuml;berwiesen (&#8364;{{ total_unterstuetzungen|floatformat:2 }})
</p>
<table>
<thead>
<tr>
<th>Destinat&auml;r</th>
<th>Betrag</th>
<th>F&auml;llig am</th>
<th>Status</th>
<th>Verwendungszweck</th>
</tr>
</thead>
<tbody>
{% for u in unterstuetzungen %}
<tr>
<td>{{ u.destinataer.get_full_name }}</td>
<td class="amount">&#8364;{{ u.betrag|floatformat:2 }}</td>
<td>{{ u.faellig_am|date:"d.m.Y" }}</td>
<td>
<span class="status-badge status-{{ u.status }}">{{ u.get_status_display }}</span>
</td>
<td>{{ u.beschreibung|default:"-" }}</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr style="font-weight: bold; background: #f0f7f4;">
<td>Summe ausgezahlt</td>
<td class="amount">&#8364;{{ total_unterstuetzungen|floatformat:2 }}</td>
<td colspan="3"></td>
</tr>
</tfoot>
</table>
</div>
{% endif %}

View File

@@ -0,0 +1,88 @@
<!-- Sektion: Pachtbericht -->
<div class="section">
<h2>Pachtbericht{% if jahr %} {{ jahr }}{% endif %}</h2>
{% if pacht_statistik %}
<div class="stats-grid">
<div class="stat-card">
<div class="value">{{ pacht_statistik.aktive_vertraege }}</div>
<div class="label">Aktive Pachtvertr&auml;ge</div>
</div>
<div class="stat-card">
<div class="value">&#8364;{{ pacht_statistik.total_pachtzins|floatformat:2 }}</div>
<div class="label">Gesamtpachtzins p.a.</div>
</div>
<div class="stat-card">
<div class="value">{{ pacht_statistik.total_flaeche|floatformat:0 }} qm</div>
<div class="label">Verpachtete Fl&auml;che</div>
</div>
<div class="stat-card">
<div class="value">{{ pacht_statistik.auslaufend_12m }}</div>
<div class="label">Laufen in 12 Mon. aus</div>
</div>
</div>
{% endif %}
{% if pacht_auslaufend %}
<h3>Auslaufende Vertr&auml;ge (n&auml;chste 12 Monate)</h3>
<table>
<thead>
<tr>
<th>L&auml;nderei</th>
<th>P&auml;chter</th>
<th>Pachtende</th>
<th>Pachtzins</th>
</tr>
</thead>
<tbody>
{% for v in pacht_auslaufend %}
<tr>
<td>{{ v.land }}</td>
<td>{{ v.paechter.get_full_name }}</td>
<td>{{ v.pachtende|date:"d.m.Y" }}</td>
<td class="amount">&#8364;{{ v.pachtzins_pauschal|floatformat:2 }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% if verpachtungen %}
<h3>Alle Verpachtungen</h3>
<table>
<thead>
<tr>
<th>L&auml;nderei</th>
<th>P&auml;chter</th>
<th>Fl&auml;che</th>
<th>Pachtzins</th>
<th>Beginn</th>
<th>Ende</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{% for v in verpachtungen %}
<tr>
<td>{{ v.land }}</td>
<td>{{ v.paechter.get_full_name }}</td>
<td class="amount">{{ v.verpachtete_flaeche|floatformat:0 }} qm</td>
<td class="amount">&#8364;{{ v.pachtzins_pauschal|floatformat:2 }}</td>
<td>{{ v.pachtbeginn|date:"d.m.Y" }}</td>
<td>{% if v.pachtende %}{{ v.pachtende|date:"d.m.Y" }}{% else %}unbefristet{% endif %}</td>
<td>
<span class="status-badge status-{{ v.status }}">{{ v.get_status_display }}</span>
</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr style="font-weight: bold; background: #f0f7f4;">
<td colspan="3">Gesamtpachtzins</td>
<td class="amount">&#8364;{{ total_pachtzins|floatformat:2 }}</td>
<td colspan="3"></td>
</tr>
</tfoot>
</table>
{% endif %}
</div>

View File

@@ -0,0 +1,30 @@
<!-- Sektion: Verwaltungskosten -->
{% if verwaltungskosten_nach_kategorie %}
<div class="section">
<h2>Verwaltungskosten {{ jahr }}</h2>
<table>
<thead>
<tr>
<th>Kategorie</th>
<th>Anzahl</th>
<th>Betrag</th>
</tr>
</thead>
<tbody>
{% for k in verwaltungskosten_nach_kategorie %}
<tr>
<td>{{ k.kategorie|capfirst }}</td>
<td>{{ k.anzahl }}</td>
<td class="amount">&#8364;{{ k.summe|floatformat:2 }}</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr style="font-weight: bold; background: #f0f7f4;">
<td colspan="2">Gesamt</td>
<td class="amount">&#8364;{{ total_verwaltungskosten|floatformat:2 }}</td>
</tr>
</tfoot>
</table>
</div>
{% endif %}