# Changelog ## [Unreleased] ### Zahlungserinnerung (Stufe 0) + frei konfigurierbare Mahnstufen - **Vor der ersten echten Mahnung steht jetzt eine kostenlose Zahlungserinnerung.** Ob eine Stufe eine Erinnerung ist, entscheidet ausschließlich das neue Flag `llx_mahnung_stufe.ist_erinnerung` — **nicht** die Stufennummer. Eine Erinnerung kostet nichts: Mahngebühr, §288-Abs.-5-Pauschale und Verzugszinsen werden in `mahnungBaueVorgang()` hart auf 0 gesetzt, unabhängig davon, was in der Stufe konfiguriert ist. - **Keine feste Obergrenze bei Stufe 3 mehr.** Stufen sind frei konfigurierbar (Nummer 0–127, Lücken erlaubt). Einstieg ist die kleinste aktive Stufe (`minStufe()`), die Folgestufe liefert `naechsteStufeNach()` — Lücken werden übersprungen. Auch der Button „Als uneinbringlich klassifizieren" hängt nicht mehr an `stufe === 3`, sondern daran, dass **keine weitere aktive Stufe** mehr folgt. - **Wartefrist korrigiert:** maßgeblich ist `frist_tage` der **Ziel**stufe, nicht `neue_frist_tage` der Vorstufe. Vorher war `frist_tage` der Stufen 2/3 ein toter Konfigurationswert. Bezugspunkt ist `date_versand`, Fallback `date_mahnung` (Bestandsdaten haben kein Versanddatum). - **Neue Spalte `llx_mahnung_mahnung.kosten_vorstufen`**: die Gebühren und Pauschalen bereits gemahnter Vorstufen fielen bisher aus der Forderung heraus, weil `rechneSumme()` nur die aktuelle Stufe addierte. - **Zinssatz-Spalten von `DECIMAL(5,4)` auf `DECIMAL(6,3)`**: `5,4` konnte maximal 9,9999 abbilden — der B2B-Standardsatz (Basiszins + 9 %) passte damit gar nicht hinein. - **`modMahnung::ensureSchema()`** zieht das Schema nach einem reinen Datei-Deploy nach (Aufruf am Kopf von `list.php`, `card.php` und den AJAX-Endpoints). Ohne das schlug nach jedem Deploy jedes `create()` fehl, bis jemand die Setup-Seite aufgerufen hatte. - **Label-Vereindeutigung** läuft genau einmal (Marker `MAHNUNG_LABEL_MIGR_DONE`) und trifft nur unveränderte Seed-Texte: Stufe 2 „1. Mahnung" → „2. Mahnung", danach Stufe 1 „Zahlungserinnerung" → „1. Mahnung". Die Reihenfolge ist zwingend, sonst hieße „1. Mahnung" doppelt. Bestandsdaten (Mahnvorgänge, Beträge) bleiben unangetastet. ### E-Mail-Versand über das Dolibarr-Mailformular (FormMail) - **Der Versand der Zahlungserinnerung läuft jetzt über das Standard-Mailformular auf der Mahnungskarte** (`card.php?id=…&action=presend`). Empfänger, Betreff, Text und Anhang sind vor dem Absenden **sichtbar und änderbar** — vorher ging die Mail nach einem bloßen Ja/Nein-Dialog ungesehen raus. - **Empfängerauswahl** enthält die Firmenadresse **und alle aktiven Ansprechpartner** des Kunden (`thirdparty_and_contact_email_array(1)`), dazu ein Freitextfeld und ein CC-Feld. Aufgelöst wird ein Empfänger nur, wenn sein Schlüssel wirklich aus der Liste **dieses** Kunden stammt: `Societe::contact_get_property()` fragt allein über die `rowid` ab und prüft die Firmenzugehörigkeit **nicht** — ein manipuliertes `receiver[]` hätte die Erinnerung sonst an einen fremden Kontakt schicken können. - **Absender ist die Firmenadresse**, nicht der angemeldete Benutzer — eine Zahlungserinnerung geht im Namen des Betriebs raus. Sie wird nur angezeigt, nicht zur Auswahl gestellt; ein trotzdem geposteter Wert wird ignoriert. - **Angehängt wird die unveränderte Original-Rechnungs-PDF**, vorbelegt beim Öffnen des Formulars (Parameter `mailinit=1`) und bei Bedarf über `Facture::generateDocument()` nacherzeugt. Weitere Dateien lassen sich anhängen und wieder entfernen. Bewusst **nicht** Dolibarrs `mode=init`: `FormMail::get_form()` leert bei diesem Parameter selbst die Anhangsliste und hätte die eingehängte Rechnung sofort wieder entfernt. - Betreff und Text kommen aus der Stufen-Konfiguration mit **bereits ersetzten Platzhaltern** (`{rechnung}`, `{summe}`, `{frist}`, `{kunde}`, `{ref}`, `{stufe}`) — im Formular stehen keine `{…}`-Marken mehr. Nachträglich ergänzte Platzhalter werden beim Absenden erneut aufgelöst. - **`ajax/sendmail.php` ist kein Endpoint mehr, sondern eine Funktionsbibliothek.** Es gibt genau **einen** Sendeweg (`mahnungSendeErinnerungsMail()`); zwei nebeneinander hätten auseinanderlaufen können. Ein Direktaufruf der Datei per HTTP verschickt nichts, sondern leitet auf das Formular um (und antwortet einem alten jQuery-Aufruf sauber mit JSON statt mit HTML). - **Doppelversand ist blockiert**: der Vorgang wird **vor** dem Senden per bedingtem `UPDATE` atomar auf VERSENDET reserviert — senden darf nur der Request, dessen UPDATE tatsächlich eine Zeile trifft. Schlägt der Versand fehl, wird ausschließlich die **eigene** Reservierung wieder zurückgenommen. - **Beträge und Frist werden zum Versandzeitpunkt frisch geprüft**: ist die Rechnung inzwischen bezahlt, storniert oder abgeschrieben, wird der Versand abgelehnt; bei einer Teilzahlung werden `betrag_offen`/`summe_mahnung` nachgezogen, damit Mail und Datensatz denselben Betrag nennen. Die neue Zahlungsfrist wird aus `neue_frist_tage` neu gerechnet — sonst nennt eine später abgeschickte Erinnerung eine bereits verstrichene Frist. - **Fachliche Sperre bleibt**: versendbar ist ausschließlich eine Stufe mit `ist_erinnerung = 1`. Echte Mahnungen gehen per Post bzw. Einschreiben raus, weil eine E-Mail nicht beweisbar zugestellt ist. ### Versandprotokoll: was ging wann an wen raus - Jede versendete Zahlungserinnerung wird protokolliert — **Empfänger, Kopie, Blindkopie, Absender, Betreff, der komplette Text und die Namen der Anhänge**, jeweils im Stand des Sendezeitpunkts. Ändert jemand später die Vorlage in der Stufen-Konfiguration, bleibt der Nachweis davon unberührt. - Einsehbar auf der Mahnungskarte unter **Versandstatus → „Versendete E-Mails"**: zugeklappt stehen Zeitpunkt und Empfänger, aufgeklappt der vollständige Mailinhalt. HTML-Mails werden formatiert dargestellt (durch `dol_string_onlythesehtmltags()`, da der Inhalt im Browser ausgegeben wird), Klartext mit erhaltenen Zeilenumbrüchen. - **Eigene Tabelle `llx_mahnung_mailprotokoll` statt Spalten am Mahnvorgang**: seit dem Wiederholversand kann dieselbe Erinnerung mehrfach rausgehen — Spalten am Vorgang würden den vorherigen Versand überschreiben, also genau den Nachweis zerstören, um den es geht. Jeder Versand ist ein eigener Eintrag, neueste zuerst. - Geschrieben wird ausschließlich nach **erfolgreichem** Versand, direkt in `mahnungSendeErinnerungsMail()` — damit kann kein Sendeweg das Protokollieren umgehen. Scheitert das Protokollieren selbst, wird das nur ins Syslog geschrieben und der Versand nicht nachträglich als gescheitert gemeldet. - Die Tabelle entsteht auf Bestandsinstallationen über die Lazy-Migration (`ensureSchema()` → `ensureMailProtokollTabelle()`), `DB_VERSION` steht jetzt auf **0.4.0**. Ein reiner Datei-Deploy genügt, es braucht keine Modul-Neuaktivierung. ### Setup: PDF-Einleitungstext nur noch, wo es ein PDF gibt - Bei Versandart **E-Mail** und bei jeder **Zahlungserinnerung** stand im Block „Texte für das Schreiben" weiterhin der **PDF-Einleitungstext** — dabei entsteht in beiden Fällen gar kein Mahnschreiben, in das er könnte (eine Erinnerung hat nie ein eigenes PDF, angehängt wird die Original-Rechnung). Das Feld wird jetzt spiegelbildlich zu den E-Mail-Feldern ein- und ausgeblendet, auch beim Umschalten der Versandart ohne Neuladen (`syncEmailRows()`). - Bewusst ausgeblendet statt entfernt: ein bereits gepflegter Einleitungstext bleibt im Formular und damit erhalten, falls die Stufe später wieder auf Postversand umgestellt wird. ### Zahnrad in die Einstellungen - Vorschlagsliste, Archiv und Mahnungskarte haben oben rechts neben dem Titel ein **Zahnrad**, das direkt in die Modul-Einstellungen führt (`mahnungSetupLink()` in `lib/mahnung_ui.lib.php`, gesetzt über den `morehtmlright`-Parameter von `load_fiche_titre()`). Sichtbar nur mit dem Recht `mahnung.setup` oder für Administratoren — wer die Seite ohnehin nicht öffnen darf, sieht kein Icon, das ihn in eine Fehlermeldung laufen lässt. ### HTML-Mails, Wiederholversand, Rechnung in der Dokumentenliste - **Mailtexte lassen sich als HTML pflegen — mit dem gewohnten Dolibarr-Editor.** Im Setup hing bisher ein nacktes `