mahnung/lib/mahnung_ui.lib.php
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

111 lines
4 KiB
PHP

<?php
/* Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
*
* GPL v3 (siehe COPYING).
*/
/**
* \file htdocs/custom/mahnung/lib/mahnung_ui.lib.php
* \ingroup mahnung
* \brief Gemeinsame Anzeige-Bausteine des Moduls.
*
* Hier liegt die EINE Darstellung einer Mahnstufe. Vorher gab es sie
* dreimal — auf der Mahnungskarte, in der Vorschlagsliste und im Widget
* — jeweils mit eigener Farbskala. Dadurch stand die Stufe an zwei
* Stellen doppelt da (farbiger Badge UND derselbe Text daneben), und die
* Farben wären beim nächsten Eingriff auseinandergelaufen.
*/
/**
* Zahnrad-Link in die Modul-Einstellungen — für den Kopf einer Modulseite.
*
* Gedacht für den Parameter $morehtmlright von load_fiche_titre(), damit das Icon
* rechts neben dem Seitentitel steht (Dolibarr-Konvention).
*
* Sichtbar nur mit dem Recht mahnung.setup oder für Administratoren. Wer die
* Einstellungen ohnehin nicht öffnen darf, bekommt kein Icon zu sehen, das ihn in
* eine "Zugriff verweigert"-Meldung laufen lässt.
*
* @return string HTML ('' = kein Recht)
*/
function mahnungSetupLink()
{
global $langs, $user;
if (!$user->hasRight('mahnung', 'setup') && empty($user->admin)) {
return '';
}
return '<a class="butActionNew" href="'.DOL_URL_ROOT.'/custom/mahnung/admin/setup.php"'
.' title="'.dol_escape_htmltag($langs->trans('MahnungSetupOeffnen')).'">'
.img_picto($langs->trans('MahnungSetupOeffnen'), 'setup', 'class="pictofixedwidth"')
.'</a>';
}
/**
* Farbe einer Mahnstufe.
*
* Maßgeblich ist das Flag ist_erinnerung der Stufe, NICHT die Stufennummer: eine
* kostenlose Zahlungserinnerung ist keine Eskalationsstufe und bleibt deshalb
* neutral grau. Stufennummern oberhalb der Standardskala sind frei konfigurierte
* Eskalationen und werden dunkelrot — sonst sähen sie harmloser aus als Stufe 3.
*
* @param int $stufe Stufennummer
* @param bool $istErinnerung true = kostenlose Zahlungserinnerung
* @return string CSS-Farbwert
*/
function mahnungStufeFarbe($stufe, $istErinnerung)
{
if ($istErinnerung) {
return '#7a7a7a';
}
// Eintrag für 0 ist nötig, weil die Stufennummer 0 auch eine echte
// (kostenpflichtige) Mahnstufe sein kann — die kostenlose Erinnerung hängt am
// Flag, nicht an der Nummer, und ist oben schon abgehandelt. Gedecktes
// Blaugrau = mildeste Eskalation; bewusst nicht heller als Stufe 1, sonst
// leidet der Kontrast zur weißen Badge-Schrift.
$colors = array(0 => '#5b7fa6', 1 => '#4a90d9', 2 => '#e68a00', 3 => '#cc3333');
if (isset($colors[(int) $stufe])) {
return $colors[(int) $stufe];
}
return ((int) $stufe > 3) ? '#a32020' : '#666';
}
/**
* Badge einer Mahnstufe — Nummer und Bezeichnung in EINEM Element.
*
* Bewusst keine zweite Textausgabe daneben: die Bezeichnung der Stufe ist frei
* konfigurierbar und heißt in der Praxis "Zahlungserinnerung", "1. Mahnung" usw.
* Ein zusätzliches Typ-Etikett würde damit nur dasselbe Wort wiederholen. Welcher
* Art die Stufe ist, transportieren Farbe und Tooltip.
*
* @param int $stufe Stufennummer
* @param string $label Bezeichnung der Stufe ('' = nur Nummer)
* @param bool $istErinnerung true = kostenlose Zahlungserinnerung
* @param string $tooltip Zusatztext für das title-Attribut ('' = Standard)
* @return string HTML
*/
function mahnungStufeBadge($stufe, $label, $istErinnerung, $tooltip = '')
{
global $langs;
$stufe = (int) $stufe;
$label = trim((string) $label);
if ($tooltip === '') {
$tooltip = $istErinnerung
? $langs->transnoentities('MahnungErinnerungKostenfrei')
: $langs->transnoentities('MahnungVorschlagBadgeMahnungHelp');
}
$text = (string) $stufe;
if ($label !== '') {
$text .= ' — '.$label;
}
$out = '<span class="badge" style="background-color:'.mahnungStufeFarbe($stufe, $istErinnerung).';color:#fff;"';
$out .= ' title="'.dol_escape_htmltag($tooltip).'">'.dol_escape_htmltag($text).'</span>';
return $out;
}