Modul: GDV-Daten / Abrechnungen / Courtage¶
Stand
Code-Analyse vom 2026-04-22 gegen sachpool.de, WebEdition 9.2.3, PHP 8.3.
Zweck¶
Dieses Modul stellt drei eng verwandte Datendienste für Vermittler bereit:
- GDV-Daten — maschinenlesbare Vertragsdaten (GDV-Format, tab-separierte Textdateien + ZIP) für Import in Makler-Software
- Beitragsinformationen — PDF-Dokumente aus manuellen Nachbearbeitungen
- Courtageabrechnungen — monatliche Courtagelisten als PDF
Alle drei teilen die gleiche Datenbank (abrechnungen_u_gdv), den gleichen
Download-Endpoint und die gleiche Reminder-Pipeline.
Template-Struktur¶
sachpool-portal/gdv-abrechnungen-courtage-daten/
├── anzeige/
│ ├── anzeige-gdvdaten.tmpl (202) Liste GDV/ZIP-Dateien
│ ├── anzeige-beitragsdaten-abrechnungen.tmpl (216) Liste PDF-Nachbearbeitungen
│ └── anzeige-courtagelisten.tmpl (134) Link zur Courtageübersicht
├── cron/
│ ├── benachrichtung/ (271) Reminder-Cron (sic! Tippfehler im Ordnernamen)
│ └── parser-datenstand/ (270) Parser-Cron für eingehende Salia-Lieferungen
├── downloadprotection/
│ └── protected-serve-file.tmpl (203) Zentraler Download-Endpoint
└── mail/
├── _inc/ (263)
├── mail-gemeinsame-signatur.tmpl (267) Sachpool-Impressum
├── mail-reminder-beitragsdaten-abrechnung.tmpl (205)
├── mail-reminder-courtageabrechnung.tmpl (266)
└── mail-reminder-gdv-daten.tmpl (265)
Einstiegspunkte (Frontend)¶
| Doc-ID | Pfad | Zeigt | Template |
|---|---|---|---|
| 547 | /_login/meine-daten/gdv-daten.php |
GDV-Dateien-Liste | 202 |
| 39508 | /_login/meine-daten/beitragsinformationen.php |
Nachbearbeitungs-PDFs | 216 |
| 566 | /_login/meine-daten/courtageliste.php |
Courtagelisten-Link | 134 |
| 14294 | /_login/_gdv-daten/ → Rewrite auf Template 203 |
Download-Endpoint | 203 |
Datenmodell: abrechnungen_u_gdv¶
Zentrale Tabelle für alle drei Datentypen. Aus Code ableitbare Felder:
| Feld | Typ | Rolle |
|---|---|---|
id |
int PK | Datensatz-ID |
user |
varchar | Vermittlernummer (entspricht $sach_user / $benutzer) |
dateiname |
varchar | Dateiname im Storage-Ordner |
extension |
varchar | gdv, zip, pdf — steuert die Anzeige-Seite |
kategorie |
varchar | u. a. Nachbearbeitung (für Beitragsinfos) |
erstelldatum |
varchar | Datum als String TT.MM.JJJJ — nicht DATE, wird per substr() geparst |
groesse |
int | Dateigröße in Bytes |
lastdownload |
varchar | Zeitstempel letzter Download als String; leer wenn nie heruntergeladen |
User-Feld-Variante mit Trailing-Underscore
Die Anzeige-Queries haben einen Fallback, wenn unter user='$benutzer' nichts
gefunden wird: user='$benutzer_' (mit angehängtem _). Historische
Namens-Konvention — Details unklar, siehe OFFENE-FRAGEN.md.
Physische Dateien¶
Download-Verzeichnisse unter /_login/:
| ID | Pfad | Inhalt |
|---|---|---|
| 2767 | /_login/_gdv-daten/ |
GDV-Dateien + Unterordner Nachbearbeitungen/ (für PDF-Nachbearbeitungen, historisch gewachsen) |
| 16667 | /_login/_beitragsinformationen/ |
PDFs (Beitragsdaten-Nachbearbeitungen) |
| 1657 | /_login/_courtagelisten-pdf/ |
Courtagelisten-PDFs |
Alle Ordner haben published = 0 → direkter HTTP-Zugriff gesperrt.
Anzeige-Templates (gemeinsames Muster)¶
anzeige-gdvdaten.tmpl (202) und anzeige-beitragsdaten-abrechnungen.tmpl (216)
haben nahezu identischen Code. Unterschiede:
| Aspekt | GDV (202) | Beitragsinfos (216) |
|---|---|---|
| WHERE-Klausel | extension='gdv' OR extension='zip' |
extension='pdf' AND kategorie='Nachbearbeitung' |
| Storage-Pfad | /_login/_gdv-daten/ |
/_login/_beitragsinformationen/ |
| Tabellen-Header | „zur GDV-Datei" | „Datei" |
Ablauf pro Zeile:
- SELECT alle Datensätze des Users (Fallback mit
user_wenn leer) - File-Existenz-Check auf Dateisystem (
file_exists()) - Wenn Datei fehlt: auto-
DELETEaus DB (self-healing) - Wenn Datei da + nie heruntergeladen: Zeile rosa hinterlegt (
#efbac6) - Table-Sorter-Output mit Filter, Pager, Download-Icon
SELECT * FROM abrechnungen_u_gdv
WHERE user='$benutzer'
AND (extension='gdv' OR extension='zip') -- für GDV
-- ODER
-- AND extension='pdf' AND kategorie='Nachbearbeitung' -- für Beitragsinfos
ORDER BY id DESC;
Letzte Aktualisierung: Maximum-Datum über alle Datensätze in
abrechnungen_u_gdv (nicht nur eigene) — für gemeinsame Aktualisierungs-Anzeige
im Footer der Tabelle.
anzeige-courtagelisten.tmpl (134) — abweichendes Muster¶
Zeigt nur einen Link zur Courtageliste. Courtagestufe des Users (aus
Session-Feld Courtage, umgesetzt zu $sach_courtage) filtert in Tabelle
courtagelisten:
Bei Treffer: Link auf {weDownloadLink}?courtage=1 (→ Download-Endpoint).
Download-Endpoint: protected-serve-file.tmpl (203)¶
Dokument-ID 14294, Template 203. Zwei Modi je nach Parameter:
Modus 1: ?courtage=1¶
Holt die aktuellste Courtagelisten-Version passend zu $sach_courtage, streamt sie.
Modus 2: ?id=<fileid> (GDV oder Beitragsinfos)¶
$sql = "SELECT dateiname, kategorie FROM abrechnungen_u_gdv WHERE id='$fileid'";
// ...
if ($kategorie == "Nachbearbeitung") {
$file = "_beitragsinformationen/" . $dateiname;
} else {
$file = "_gdv-daten/" . $dateiname;
}
// KRITISCHE Zugriffsprüfung:
if (strpos($file, (string)$sach_user) !== false) {
Download($internal_path . $web_path . $file);
}
Schutz-Pipeline (3 Walls)¶
- WebEdition-Login (Workspace 536 erforderlich)
.htaccess DENY ALLin den_gdv-daten/,_beitragsinformationen/,_courtagelisten-pdf/-Verzeichnissen — direkter Browser-Zugriff nicht möglich- Vermittlerkennung im Dateinamen:
strpos($file, $sach_user)prüft, dass die Datei die Kennung des Users enthält
Security: IDOR-Vektor in Wall 3
strpos !== false entspricht „enthält als Substring", nicht „gehört zu User".
Bei Vermittler 90100-11 würde eine Datei dateifuer_90100-111.pdf fälschlich
durchgelassen. → interne Datei HANDLUNGSEMPFEHLUNGEN.md
Download-Streaming¶
Eigene Download()-Funktion mit:
HTTP 206 Partial ContentbeiRange-Headern (Resume-fähig, gute Praxis!)Cache-Control: public, no-cache1024 KB/sDefault-Rate-Limit ($speed)set_time_limit(0)+session_write_close()für lange Downloadsconnection_status() === CONNECTION_NORMALin Schleife, saubere Cleanup-Semantik
Nebenwirkung: UPDATE lastdownload¶
Bei erfolgreichem GDV/Beitragsinfo-Download:
Darum verschwindet die rosa Markierung auf der Anzeigeseite nach dem ersten Download.Parser-Cron (270)¶
Verarbeitet eingehende Salia-Lieferungen. Details des Parser-Templates werden in
einer späteren Runde nachgezogen — siehe interne Datei OFFENE-FRAGEN.md.
Reminder-Mails¶
Pro Datentyp ein eigenes Template + gemeinsame Signatur (mail-gemeinsame-signatur.tmpl,
ID 267) mit Sachpool-Impressum + rechtliche Standard-Fußzeile.
Die gemeinsame Signatur enthält u. a.: - HRB 12171 – Chemnitz - Geschäftsführer: R. André Klotz - Steuer-Nr.: 218/118/04018 - IHK-Reg.Nr: D-0CXQ-33EW8-28
Bei Änderung dieser Firmendaten müssen alle Mail-Templates aktualisiert werden.
Bekannte Altlasten¶
→ interne Datei HANDLUNGSEMPFEHLUNGEN.md im Repo-Root
- 🔴🔴🔴 IDOR im
protected-serve-file(Substring-Check) - 🔴 SQL-Konkatenation in allen Anzeige- und Serve-Templates
- 🟢 Tippfehler-Ordner
benachrichtungstattbenachrichtigung - 🟡 Download nicht transaktionssicher (historisch)
- 🟡 Datum als String-Feld in DB,
substr()-Parsing für Sortierung - 🟡 Sachpool-Firmenangaben hartkodiert in
mail-gemeinsame-signatur.tmpl
Siehe auch¶
- Datenbank-Zugriff
- Vertragsauskunft — nutzt
abrechnungsuebersicht.php - Cron-Jobs
- Nutzer: Meine Daten