mahnung/README.md
Eduard Wisch 0244a5b07e feat(mahnung): Mailversand ueber FormMail, Versandprotokoll, HTML-Mails
Phase 13/14 abgeschlossen. Der Mailversand war zuvor KAPUTT: ajax/sendmail.php
war bereits zur Funktionsbibliothek umgebaut, card.php postete aber weiterhin
per JS dagegen.

Mailformular (FormMail)
- card.php nutzt jetzt Dolibarrs Standard-Mailformular (action=presend/send):
  Empfaenger (Firma + alle Ansprechpartner), Betreff, Text und Anhang sichtbar
  und aenderbar. Gesendet wird ausschliesslich ueber mahnungSendeErinnerungsMail().
- Anhang = unveraenderte Original-Rechnungs-PDF, wird bei Bedarf nacherzeugt.
  Eigener Parameter mailinit statt mode=init, weil get_form() bei mode=init die
  Anhangsliste selbst leert.
- HTML-Mails: DolEditor im Setup + withfckeditor=-1 im Formular (folgt
  FCKEDITOR_ENABLE_MAIL wie Dolibarrs eigene Mailvorlagen).
- Klartext bleibt Klartext: GETPOST('restricthtml') jagt jeden Nicht-HTML-Text
  durch dol_nl2br() — mahnungBodyEntkleiden() nimmt nur dieses Artefakt zurueck
  und laesst echte Formatierung unangetastet.
- Platzhalter jetzt auch in Dolibarr-Schreibweise (__REF__, __DATE_YMD__,
  __AMOUNT_FORMATED__, __DATE_DUE_YMD__, __FRIST_TAGE__ ...), Liste sichtbar im Setup.
- Absender-Adresse und -Name konfigurierbar (MAHNUNG_EMAIL_SENDER[_NAME]).
- Erneuter Versand moeglich (force aus dem Status abgeleitet, nicht aus dem
  Request — Doppelversand-Schutz bleibt wirksam).

Versandprotokoll (neue Tabelle llx_mahnung_mailprotokoll)
- Jede versendete Erinnerung wird mit Empfaenger, Kopie, Betreff, Text und
  Anhangsnamen festgehalten, einsehbar unter Versandstatus. Historie statt
  Spalten am Vorgang, weil erneut gesendet werden kann.
- Lazy-Migration legt die Tabelle an (DB_VERSION 0.4.0), kein Reaktivieren noetig.

Haertung nach Code-Review (21 bestaetigte Funde)
- Anhang liess sich nicht abwaehlen (wurde sofort wieder eingehaengt)
- Upload/Entfernen ohne Rechtepruefung; Temp-Verzeichnis pro Vorgang getrennt
- Teilzahlung zwischen Oeffnen und Senden fuehrt zurueck ins Formular
- Empfaenger: Semikolon-Trenner, keine stillen Verwerfungen, Dubletten, CR/LF
- CSRF: presend + Core-Dateiaktionen (confirm_deletefile, renamefile, sendit,
  linkit) token-pflichtig
- Externe Benutzer sehen nur eigene Vorgaenge; Abschreiben verlangt facture.creer

UI
- Mahnstufe nur noch EINE Darstellung (Badge), Farbskala zentral in
  lib/mahnung_ui.lib.php statt doppelt gepflegt
- Zahnrad oben rechts in die Einstellungen (nur mit Recht mahnung.setup)
- Original-Rechnung unter Verknuepfte Dokumente mit Vorschau, Groesse in KB
- PDF-Einleitungstext nur noch, wo ueberhaupt ein PDF entsteht

Sprachdateien de_DE/en_US deckungsgleich, 9 tote Keys entfernt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 21:31:25 +02:00

292 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Modul Mahnung — Mahnwesen für Dolibarr
Mahnwesen-Modul für Dolibarr ERP: tägliche Vorschlagsliste überfälliger Rechnungen, frei konfigurierbare Mahnkette mit einer kostenlosen Zahlungserinnerung davor, Mahngebühren + tagesgenaue Verzugszinsen nach **BGB §288**, PDF- und ODT-Mahnschreiben, E-Mail-Versand über das Dolibarr-Mailformular, Sammelbrief, Trigger auf Zahlungseingang.
## Status
Version **0.3.0 (Unreleased)** — Zahlungserinnerung als kostenlose Stufe 0, frei konfigurierbare Stufen, Mailversand über Dolibarrs `FormMail`, Setup-Seite als Akkordeon. Davor: UX-Vorschlagsliste, Versand-Erfassung, Sendebeleg-Upload, konfigurierbare Tracking-Patterns mit Live-Vorschau, Beleg-Scan via pdftotext, Versand-Reminder, Bonitäts-Workflow + Uneinbringlich-Klassifikation.
## Grundprinzip der Mahnkette
```
Fälligkeit überschritten
└─ Stufe 0 Zahlungserinnerung sofort, kostenlos, nur E-Mail
└─ Wartefrist (frist_tage der Zielstufe, gerechnet ab date_versand)
└─ Stufe 1 1. Mahnung Gebühr + Zinsen, Post/Einschreiben
└─ Stufe 2 … weitere Stufen frei konfigurierbar
└─ uneinbringlich (§288-Abschreibung)
```
- **Ob eine Stufe eine kostenlose Erinnerung ist, entscheidet das Flag `ist_erinnerung` — nicht die Stufennummer.** Eine Erinnerung kostet nichts (keine Mahngebühr, keine §288-Abs.-5-Pauschale, keine Verzugszinsen) und hat kein eigenes Mahn-PDF.
- **Erinnerungen gehen ausschließlich per E-Mail raus, echte Mahnungen ausschließlich per Post/Einschreiben.** Der Zugang einer E-Mail ist nicht beweisbar — das ist als Sperre im Code verdrahtet, nicht bloß Konvention.
- **Kein Mindestmaß an Stufen:** §286 BGB verlangt keine drei Mahnungen, eine genügt für den Verzug. Stufennummern sind frei wählbar (0127), Lücken erlaubt; die Kette läuft aufsteigend über die aktiven Stufen.
## Features
| Feature | Implementierung |
|---|---|
| Tägliche Vorschlagsliste überfälliger Rechnungen | Cron `MahnungCronBuildVorschlag` |
| Push-Benachrichtigung mit Anzahl je Stufe | Ntfy + GlobalNotify |
| **Beliebig viele Stufen** pflegbar (Nummer, Frist, neue Frist, Gebühr B2C/B2B, Versandart, E-Mail-Template, PDF-Intro), Setup als Akkordeon | `admin/setup.php` |
| **Kostenlose Zahlungserinnerung** als eigene Stufe (Flag `ist_erinnerung`) — Gebühr, Pauschale und Zinsen hart auf 0 | `mahnungBaueVorgang()` in `lib/mahnung_anlage.lib.php` |
| B2C / B2B-Erkennung | über `llx_societe.tva_intra` |
| Verzugszinsen | tagesgenau, B2C: Basiszins +5 %, B2B: +9 %; Override pro Stufe möglich; **auto-Neuberechnung** bei noch nicht versandten Mahnungen |
| §288 Abs. 5 Pauschale 40 € | nur bei B2B, einmalig pro Rechnung |
| PDF-Mahnschreiben (DIN 5008) | Standard-TCPDF-Generator `pdf_standard_mahnung` |
| ODT-Mahnschreiben (je Stufe) | ODT-Template-Generator `doc_generic_mahnung_odt` mit Stufen-Auswahl |
| Dokumentenmodell-Verwaltung | Setup-Seite: Modelle aktivieren, Default wählen, ODT-Templates hochladen |
| Template-Variablen-Referenz | `admin/templatevars.php` — alle verfügbaren ODT-Variablen |
| Dokumentenliste auf Mahnungs-Karte | Vorschau (Lupe), Download, Modellauswahl |
| Bulk-Erstellung + Sammelbrief | TCPDI-basiert |
| **E-Mail-Versand der Zahlungserinnerung über Dolibarrs Mailformular** — Empfänger (Firma + alle Ansprechpartner), Betreff, Text und Anhang vor dem Absenden sichtbar und änderbar | `card.php?action=presend` mit `FormMail`; gesendet wird ausschließlich über `mahnungSendeErinnerungsMail()` |
| Anhang = **unveränderte Original-Rechnungs-PDF** (wird bei Bedarf nacherzeugt), weitere Dateien anhängbar | `mahnungHoleRechnungsPdf()` |
| Schutz gegen Doppelversand | atomare Reservierung per bedingtem `UPDATE` **vor** dem Senden |
| Auto-Erledigung bei Zahlungseingang | Trigger `BILL_PAYED` + `PAYMENT_CUSTOMER_CREATE` |
| Tab "Mahnungen (n)" auf Rechnungs- + Kundenkarte | Hook `completeTabsHead` (filtert auf add/external) |
| Widget: Offene Rechnungen mit Mahnstufe (Zähler im Kopf, Zeilenanzahl einstellbar, Summenzeile Netto+Brutto) | `box_mahnung_offen`, Konstante `MAHNUNG_BOX_MAXLINES` |
| **Widget-Spalte „Vsl. Zahlung" (Zahlungsprognose)**: Prognosedatum (Rechnungsdatum + Ø Tage nach Rechnungseingang) + Zahler-Ampel; Bewertungsskala 1:1 aus BuchhaltungsWidget/Kundenkarte, verstrichene Prognose = rotes Datum | `box_mahnung_offen` (`getZahlprognose`/`buildPrognoseCell`), Konstante `MAHNUNG_PROGNOSE_MIN_N` |
| **Vorschlagsliste-Filter** (Mahnstufe, Tage Verzug, Mindestbetrag, Kundentyp B2B/B2C, Kunden-Select2 mit Ajax) | `list.php` — Kundentyp-Wechsel filtert das Kunden-Dropdown sofort per Auto-Submit |
| **Kontakt-Spalte** in Vorschlagsliste (Telefon-/Mail-Icons mit Direktlink) | tel:/mailto: aus `llx_societe.phone/email` |
| **Versand-Erfassung** pro Mahnung (Datum, Versandweg, Sendungsnummer, Tracking-Provider) | `card.php` Versand-Block; setzt automatisch Status auf VERSENDET |
| **Tracking-Deep-Links** für DHL, DPAG, DPD, Hermes, UPS | konfigurierbar via Tracking-Pattern-Tabelle |
| **Sendebeleg-Upload** pro Mahnung | Dolibarrs `formfile->showdocuments('mahnung', ...)``DOL_DATA_ROOT/mahnung/<MAHN-Ref>/` |
| **Konfigurierbare Tracking-Patterns** (Regex + URL-Template, Priorität, Aktivierung) | `admin/tracking_patterns.php` CRUD + Live-Vorschau |
| **Live-Regex-Vorschau** (Treffer + URL-Preview während Eingabe) | `ajax/regex_preview.php` (debounced 300ms, ReDoS-Schutz) |
| **Beleg-Scan** (pdftotext + ocrmypdf-Fallback + Pattern-Matching → Sendungsnummer-Vorschlag) | Button "Belege scannen" auf `card.php`; "Übernehmen" setzt automatisch Tracking + Versanddatum + Versandweg |
| **Versand-Reminder** (Mahnung > N Tage unversendet → Ntfy-Push) | Cron `MahnungCronVersandReminder`, Schwellenwert via `MAHNUNG_VERSAND_REMINDER_DAYS` |
| **Bonitäts-Box auf Kundenkarte** (Anzahl + Summe + letztes Datum der `badcustomer` abandoned Rechnungen) | Hook `tabContentViewThirdparty` |
| **Bonitäts-Warning** bei Auftrag-/Rechnungs-Karte | Hook `formObjectOptions` (invoicecard + ordercard) |
| **"Als uneinbringlich klassifizieren"-Button** auf Mahnung Stufe 3 | Bestätigungs-Dialog mit Begründung → `Facture::setCanceled($user, CommonInvoice::CLOSECODE_BADDEBT, $note)` |
## Voraussetzungen
- Dolibarr ≥ 19
- PHP ≥ 7.4
- TCPDF (Dolibarr-Standard) für PDF
- Optional: ODTPHP (Dolibarr-Standard) für ODT-Templates
- Optional: TCPDI (Dolibarr `includes/tcpdf/tcpdi.php`) für Sammelbrief-Konkatenation
- Optional: `pdftotext` + `ocrmypdf` + `tesseract` im Container für OCR-Beleg-Scan
- Optional: `GlobalNotify` für In-App-Notification-Badges
- Empfohlen: `BankImport` (automatischer Zahlungseingang via FinTS triggert die Mahnungs-Erledigung sauber)
## Installation
```bash
git clone https://git.data-it-solution.de/data/mahnung.git \
/pfad/zu/dolibarr/htdocs/custom/mahnung
```
Dann **Startseite → Setup → Module → Finanzwesen → Mahnung** aktivieren.
Bei Aktivierung werden `llx_mahnung_mahnung` und `llx_mahnung_stufe` angelegt; drei Default-Stufen werden idempotent eingefügt. Dokumentenmodelle (`standard_mahnung`, `generic_mahnung_odt`) werden in `llx_document_model` registriert.
Nach Aktivierung in **Setup → Mahnwesen Einstellungen**:
1. Basiszins prüfen
2. Stufen-Texte (PDF-Intro, E-Mail-Body) anpassen
3. Dokumentenmodell wählen und ggf. ODT-Templates hochladen
4. Cron-Job `MahnungCronBuildVorschlag` aktivieren
## ODT-Templates
Templates werden unter **Setup → Mahnwesen Einstellungen → Dokumentenmodelle** hochgeladen.
### Dateinamen-Konvention
| Dateiname | Verwendung |
|---|---|
| `mahnung_stufe1.odt` | Zahlungserinnerung (Stufe 1) |
| `mahnung_stufe2.odt` | 1. Mahnung (Stufe 2) |
| `mahnung_stufe3.odt` | Letzte Mahnung (Stufe 3) |
| `mahnung.odt` | Fallback für alle Stufen |
### Verfügbare Template-Variablen (Auswahl)
| Variable | Beispiel |
|---|---|
| `{mahnung_ref}` | MAHN2026-0001 |
| `{mahnung_stufe}` | 1 |
| `{mahnung_stufe_label}` | Zahlungserinnerung |
| `{mahnung_date}` | 10.05.2026 |
| `{mahnung_betrag_offen}` | 131,34 |
| `{mahnung_mahngebuehr}` | 5,00 |
| `{mahnung_verzugszinsen}` | 1,23 |
| `{mahnung_summe}` | 137,57 |
| `{mahnung_kundentyp}` | B2C |
| `{facture_ref}` | IN2604-0036 |
| `{facture_total_ttc}` | 131,34 |
| `{company_name}` | Kundenname |
| `{mycompany_name}` | Eigene Firma |
| `{mahnung_bank_iban}` | DE89... |
Vollständige Liste: **Setup → Mahnwesen → Verfügbare Template-Variablen**
## Rechte
| Recht | Standard | Bedeutung |
|---|---|---|
| `mahnung.read` | aktiv | Mahnungen einsehen |
| `mahnung.write` | inaktiv | Mahnungen erstellen / bearbeiten |
| `mahnung.send` | inaktiv | Mahnungen versenden (E-Mail / Druck) |
| `mahnung.delete` | inaktiv | Mahnungen stornieren |
| `mahnung.setup` | inaktiv | Stufen, Basiszins, Versand konfigurieren |
## Modul-Konstanten
| Name | Default | Bedeutung |
|---|---|---|
| `MAHNUNG_BASISZINS` | `1.27` | BGB-Basiszins (%) — halbjährlich pflegen (1.1./1.7.) |
| `MAHNUNG_AUFSCHLAG_B2C` | `5.0` | Verzugszins-Aufschlag B2C (BGB §288 Abs. 1) |
| `MAHNUNG_AUFSCHLAG_B2B` | `9.0` | Verzugszins-Aufschlag B2B (BGB §288 Abs. 2) |
| `MAHNUNG_PAUSCHALE_B2B` | `40.00` | Pauschale B2B (EUR, BGB §288 Abs. 5, einmalig) |
| `MAHNUNG_ADDON_PDF` | `standard_mahnung` | Standard-Dokumentenmodell |
| `MAHNUNG_ADDON_PDF_ODT_PATH` | `DOL_DATA_ROOT/doctemplates/mahnung` | ODT-Template-Verzeichnis |
| `MAHNUNG_NTFY_TOPIC` | `vk-builds` | Ntfy-Topic für Vorschlags-Push |
| `MAHNUNG_VERSAND_REMINDER_DAYS` | `2` | Schwelle (Tage) für Versand-Reminder Ntfy-Push |
| `MAHNUNG_BOX_MAXLINES` | `0` | Widget: max. angezeigte offene Rechnungen (`0` = alle) |
| `MAHNUNG_PROGNOSE_MIN_N` | `1` | Widget „Vsl. Zahlung": min. bezahlte Rechnungen für eine Prognose (`1` = Parität zur Kundenkarten-Box) |
## Datenbank
| Tabelle | Zweck |
|---|---|
| `llx_mahnung_mahnung` | Mahnvorgänge — inkl. Versand-Felder `date_versand`, `versandweg`, `tracking_nr`, `tracking_provider` (idempotente Migration via `migrateVersandFelder()` beim Setup-Aufruf) |
| `llx_mahnung_stufe` | Stufen-Konfiguration (beliebig viele Stufen mit Fristen, Gebühren, Templates; `ist_erinnerung` markiert die kostenlose Zahlungserinnerung) |
| `llx_mahnung_trackingpattern` | Konfigurierbare Tracking-Regex + URL-Template pro Provider, Default-Seed mit DHL/DPAG/DPD/Hermes/UPS |
## AJAX-Endpoints
| Endpoint | Zweck | Parameter | Auth |
|---|---|---|---|
| `ajax/createmahnung.php` | Bulk-Mahnung-Erzeugung | `facture_ids[]`, `stufe`, `token` | `mahnung.write` |
| `ajax/sammelbrief.php` | Sammelbrief-PDF | `facture_ids[]`, `token` | `mahnung.send` |
| `ajax/regex_preview.php` | Live-Vorschau für Tracking-Patterns | `regex`, `sample`, `url_template` (POST) | `mahnung.setup` oder Admin |
> `ajax/sendmail.php` ist **kein Endpoint mehr**, sondern die Funktionsbibliothek des Mailversands (Empfängerermittlung, Rechnungsprüfung, Anhang, Platzhalter, Statuswächter, Versand-Reservierung, `CMailFile`-Aufruf). Verschickt wird ausschließlich über das Mailformular auf `card.php`. Ein Direktaufruf per HTTP sendet nichts, sondern leitet dorthin um.
## Hooks & Trigger
**Hook-Klasse:** `class/actions_mahnung.class.php` — registriert für `invoicecard`, `thirdpartycard`, `ordercard`.
| Hook | Zweck |
|---|---|
| `addMoreActionsButtons` | "Mahnung erstellen"-Button auf Rechnungs-Karte |
| `completeTabsHead` | Tab "Mahnungen (n)" — filtert auf `mode=add` + `filterorigmodule=external` (sonst doppelter Tab, siehe KB #601) |
| `tabContentViewThirdparty` | Bonitäts-Warnbox auf Kundenkarte wenn `badcustomer` abandoned Rechnungen existieren |
| `formObjectOptions` | Bonitäts-Warning bei Auftrag-/Rechnungs-Karten |
**Trigger:** `core/triggers/interface_99_modMahnung_MahnungTriggers.class.php`
- `BILL_PAYED` + `BILL_VALIDATE` + `PAYMENT_CUSTOMER_CREATE` → setzt offene Mahnungen auf `STATUS_ERLEDIGT`
**Cron-Jobs:**
- `MahnungCronBuildVorschlag` — täglich, Vorschlagsliste aufbauen + Ntfy-Push
- `MahnungCronVersandReminder` — täglich, Reminder für unversendete Mahnungen
## Workflow
```
Cron 06:00 (MahnungCronBuildVorschlag)
|
|--> ueberfaellige Rechnungen einsammeln
|--> Stufe ermitteln (Einstieg = kleinste aktive Stufe; Folgestufe, sobald
| frist_tage der ZIELstufe seit date_versand der
| Vorstufe verstrichen sind, Luecken werden uebersprungen)
|--> Ntfy-Push mit Anzahl je Stufe + Gesamt-EUR
|--> GlobalNotify "actionRequired" (wenn Modul aktiv)
|
v
list.php (Vorschlagsliste)
| Filter: Mahnstufe, min. Verzug, min. Betrag, Kundentyp (B2B/B2C), Kunde (Select2)
| Spalten: Rechnung, Kunde, Kontakt (tel/mail), Typ, Faelligkeit, Verzug,
| Betrag, letzte Mahnung, vorgeschlagene Stufe
|
|--> User waehlt aus + klickt "Mahnungen erzeugen"
| -> ajax/createmahnung.php berechnet Gebuehr + Verzugszinsen,
| persistiert llx_mahnung_mahnung, generiert PDF/ODT
|
|--> Alternativ "Sammelbrief erzeugen"
| -> ajax/sammelbrief.php generiert + konkateniert PDFs
|
v
card.php (Mahnung-Detailansicht)
| Documents: generierte PDFs + Modellauswahl + Regenerate
| (bei einer Zahlungserinnerung entfaellt das - kein eigenes Mahn-PDF)
| Bei Versandart E-Mail: nur Versandstatus (Empfaenger, versendet am)
| Bei Postversand: Datum, Versandweg, Sendungsnummer, Tracking-Deep-Link,
| Sendebeleg-Upload + Scan-Button
| Aktionen: Stornieren
| | Zahlungserinnerung per E-Mail senden (nur ist_erinnerung-Stufen)
| -> action=presend: Dolibarr-Mailformular mit Empfaengerauswahl
| (Firma + Ansprechpartner), Betreff, Text, Original-Rechnung
| als Anhang; action=send verschickt ueber
| mahnungSendeErinnerungsMail() (atomare Reservierung)
| | Als uneinbringlich klassifizieren (wenn keine weitere Stufe folgt)
|
Cron 06:00 (MahnungCronVersandReminder)
|--> Mahnungen mit Status=ERSTELLT > N Tage unversendet -> Ntfy-Push
|
Zahlungseingang (BankImport / Manual)
|
v Trigger BILL_PAYED / PAYMENT_CUSTOMER_CREATE
|--> alle offenen Mahnvorgaenge zur Rechnung -> status=erledigt
|
Endgueltig uneinbringlich (Mahnbescheid + Vollstreckung erfolglos)
|
v letzte Mahnstufe -> Button "Als uneinbringlich klassifizieren"
|--> Facture::setCanceled($user, CLOSECODE_BADDEBT, $note)
| -> fk_statut=3 (ABANDONED), close_code='badcustomer'
| -> Mahnung wird storniert, Begruendung in note_private
|--> Auf Kundenkarte erscheint rote Bonitaets-Warnbox
|--> Beim Anlegen neuer Auftraege/Rechnungen: Warning-Banner
|
Steuerlich (Steuer-Modul):
|--> EÜR ignoriert (liest llx_paiement, keine Zahlung = keine Einnahme)
|--> UStVA filtert fk_statut IN (1,2) -> abandoned automatisch ausgeschlossen
```
## Dateibaum
```
mahnung/
├── core/modules/modMahnung.class.php Descriptor, Rechte, Cron, Hooks, Models
│ (migrateVersandFelder + Pattern-Seed)
├── core/modules/mahnung/
│ ├── modules_mahnung.php Abstrakte Basis ModelePDFMahnung
│ └── doc/
│ ├── pdf_standard_mahnung.modules.php TCPDF-Generator (DIN 5008)
│ └── doc_generic_mahnung_odt.modules.php ODT-Template-Generator
├── core/triggers/interface_99_modMahnung_MahnungTriggers.class.php
├── core/boxes/box_mahnung_offen.php Widget: Offene Rechnungen + Mahnstufe
├── class/mahnung.class.php CRUD Mahnvorgang + generateDocument()
│ + setVersand + trackingUrl + defaultProviderForWeg
├── class/mahnungstufe.class.php CRUD Stufen-Konfig
├── class/mahnungvorschlag.class.php Vorschlags-Service (Stufenermittlung,
│ kundentyp/min_betrag-Filter, soc_phone/email)
├── class/mahnungtrackingpattern.class.php CRUD Tracking-Patterns + detectFromText
├── class/mahnungcron.class.php buildVorschlagsliste + versandReminder
├── class/mahnungntfy.class.php Ntfy-Wrapper
├── class/actions_mahnung.class.php Hook-Klasse:
│ - addMoreActionsButtons (invoicecard)
│ - completeTabsHead (invoice+thirdparty)
│ - tabContentViewThirdparty (Bonitaets-Box)
│ - formObjectOptions (invoice+ordercard Warning)
├── admin/setup.php Setup + Dokumentenmodell + Auto-Migration
├── admin/templatevars.php Template-Variablen-Referenz
├── admin/tracking_patterns.php CRUD + Live-Vorschau fuer Tracking-Patterns
├── ajax/createmahnung.php Bulk-Mahnung-Erzeugung
├── ajax/sammelbrief.php Sammelbrief-PDF-Konkatenation
├── ajax/sendmail.php Funktionsbibliothek Mailversand
│ (KEIN Endpoint - siehe AJAX-Abschnitt)
├── ajax/regex_preview.php Live-Vorschau-Endpoint fuer Pattern-Setup
├── lib/mahnung_anlage.lib.php Anlage eines Mahnvorgangs (mahnungBaueVorgang)
├── lib/mahnung_ui.lib.php Stufen-Badge + Farbskala (eine Quelle fuer
│ card.php, list.php und das Widget)
├── list.php Vorschlagsliste / Archiv (Select2-Filter)
├── card.php Detailansicht: Versand, Belege, Tracking,
│ Mailformular (FormMail), Uneinbringlich-Workflow
├── sql/llx_mahnung_*.sql Schema + Seed
└── langs/{de_DE,en_US}/mahnung.lang
```
## Lizenz
GPL-3.0