All checks were successful
Deploy mahnung / deploy (push) Successful in 14s
tms war als reines TIMESTAMP angelegt -> unter explicit_defaults_for_timestamp NULL DEFAULT NULL, blieb daher bei jedem UPDATE leer. Jetzt Dolibarr-Standard DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP fuer mahnung/stufe/ trackingpattern. Neue idempotente Migration migrateTimestampSpalten() im init() befuellt Alt-NULLs aus datec und stellt die Spalte per ALTER TABLE um. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
14 KiB
14 KiB
Changelog
[Unreleased]
Schema-Fix (tms-Spalten)
- Die
tms-Spalten vonllx_mahnung_mahnung,llx_mahnung_stufeundllx_mahnung_trackingpatternwurden als reinesTIMESTAMPangelegt — unterexplicit_defaults_for_timestampentstand darausNULL DEFAULT NULL, sodasstmsbei jedem UPDATE leer blieb. Jetzt Dolibarr-StandardDEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, damit der Änderungszeitpunkt (z.B. einer per Zahlungstrigger erledigten Mahnung) wieder nachvollziehbar ist. - Neue idempotente Migration
migrateTimestampSpalten()(läuft im Modul-init()): befüllt bestehendeNULL-Werte ausdatecund stellt die Spalte perALTER TABLEum. Greift auf Bestands-Installs beim Re-Aktivieren des Moduls.
Bonitaet / Forderungsausfall-Workflow
- Neuer Hook
tabContentViewThirdpartyrendert auf der Kundenkarte eine prominente rote Warnbox, wenn der Kunde abgeschriebene Rechnungen (fk_statut=3+close_code='badcustomer') hat. Zeigt Anzahl, Gesamtsumme, Datum der letzten Abschreibung + Link zur Detail-Liste. - Neuer Hook
formObjectOptionszeigt eine kompakte Warn-Zeile bei Auftrags-/Rechnungs-Karten ("ordercard", "invoicecard"), wenn der Kunde Forderungsausfaelle hat — Bonitaets-Pruefung vor neuem Geschaeft. - Neuer Hook-Kontext
ordercardzum Modul-Descriptor ergaenzt. - Neuer Button "Als uneinbringlich klassifizieren" auf Mahnung-Karten der Stufe 3 (Status ≥ ERSTELLT, nicht storniert, Rechnung nicht bereits abandoned).
- Bestaetigungs-Dialog mit Begruendungs-Textfeld (Default: "Mahnverfahren erfolglos abgeschlossen am ...")
- Ruft
Facture::setCanceled($user, CommonInvoice::CLOSECODE_BADDEBT, $note)→ Rechnung auffk_statut=3+close_code='badcustomer' - Mahnung wird zugleich storniert und die Begruendung in
note_privatefestgehalten
- Steuerlich passt das: Dolibarrs Steuer-Modul (EÜR) ignoriert abandoned Rechnungen automatisch (UStVA filtert
fk_statut IN (1,2); EÜR liest nurllx_paiement= tatsaechliche Zahlungen). Bei Ist-Versteuerung ist damit alles korrekt — keine separate Buchung noetig.
UX-Fixes (Vorschlagsliste)
- Kundentyp-Filter (B2B/B2C) wird jetzt direkt an
select_company()durchgereicht — wenn B2C gewaehlt ist, zeigt das Kunden-Dropdown nur noch Drittparteien ohne TVA-Nummer (entsprechend umgekehrt fuer B2B). Filter nutzt Dolibarrs Universal-Search-Criteria-Syntax(feld:operator:wert)— plain SQL wuerde durchforgeSQLFromUniversalSearchCriteriafehlschlagen. - Auto-Submit beim Wechsel des Kundentyps + automatisches Reset von
search_socid, damit das Dropdown ohne extra "Suche"-Klick aktualisiert wird und keine ungueltige (im neuen Filter nicht enthaltene) Kunden-ID stehen bleibt. search_socid=-1(vonselect_companyals "nichts ausgewaehlt" geliefert) wird jetzt korrekt ignoriert statt als Filter auffk_soc=-1zu wirken.- Skip-Grund-Spalte in der Uebersprungen-Tabelle:
opacitymediumjetzt am innerenspanstatt amtd, damit Theme-spezifisches Zellen-Border-Verhalten konsistent bleibt.
Versand-Reminder (Cron + Ntfy)
- Neuer Cron-Job
MahnungCronVersandReminder(taeglich): sucht Mahnungen mit StatusERSTELLTderen PDF seit > N Tagen erstellt aber noch nicht versendet wurde, schickt Ntfy-Push und (falls aktiv) GlobalNotify-Badge. - Schwellenwert konfigurierbar via Konstante
MAHNUNG_VERSAND_REMINDER_DAYS(Default 2). - Nachricht listet bis zu 8 Mahnungen (Ref + Stufe + Alter in Tagen + Kunde); Rest als "+N weitere".
Beleg-Scan mit Sendungsnummer-Erkennung
- Neuer Button "Belege scannen" im Versand-Block der Mahnungs-Karte.
- Beim Klick werden alle hochgeladenen Belege (PDF via
pdftotext, sonst txt/html) durchsucht und gegen die konfigurierten Tracking-Patterns gematcht. - OCR-Fallback: wenn
pdftotextnur Form-Feed-Zeichen liefert (Bild-PDF), wird automatischocrmypdf --skip-text -l deu+engaufgerufen und das OCR-PDF erneut mitpdftotextgelesen. Temporaeres OCR-PDF wird nach Extraktion geloescht. - Erkannte Sendungsnummern werden als Vorschlag ueber dem Beleg-Bereich angezeigt (mit Dateiname, Provider-Label, Sendungsnummer + Deep-Link).
- Per "Uebernehmen"-Button werden
tracking_nr+tracking_providergespeichert; zusaetzlich werdendate_versandundversandwegautomatisch gesetzt (falls noch leer), sodass kein separates Speichern noetig ist. - "Verwerfen" entfernt den Vorschlag aus der Session.
- DPAG-Einschreiben-Regex erlaubt OCR-typische Leerzeichen zwischen Zifferngruppen (
R[A-Z]\s?\d{4}\s?\d{4}\s?\d\s?DE).
Konfigurierbare Tracking-Patterns (Setup-Seite)
- Neue Tabelle
llx_mahnung_trackingpattern(Pro Eintrag: provider, label, regex, url_template, priority, active). Auto-Migration + Default-Seed beim Setup-Aufruf. - Default-Patterns: DHL Paket (20-stellig), DPAG Einschreiben (
RR123456789DE), UPS (1Z…), DHL 11-stellig, Hermes 14-stellig, DPD 14-stellig — Prioritaeten so gesetzt dass spezifischere Patterns zuerst greifen. - Neue Setup-Seite
admin/tracking_patterns.phpmit CRUD: Pattern anlegen/bearbeiten/aktivieren-deaktivieren/loeschen. - Live-Vorschau: Beim Tippen von Regex/URL/Beispieltext wird via AJAX-Endpoint
ajax/regex_preview.phpdirekt gezeigt ob der Regex syntaktisch gueltig ist, was er aus dem Beispieltext matcht und wie die finale Tracking-URL aussieht. - ReDoS-Schutz im AJAX-Endpoint: max 10 KB Sample,
pcre.backtrack_limit=100k, Whitelist Delimiter/ # ~. Mahnung::trackingUrl()(hardcoded Fallback) bleibt — primaer wirdMahnungTrackingPattern::urlFor()aus DB-Patterns benutzt.- Setup-Page-Link: Button "Tracking-Muster (Regex)" oben rechts auf der Modul-Setup-Seite.
Versand & Belege (Mahnungs-Karte)
- Neue Felder
date_versand,versandweg,tracking_nr,tracking_provideranllx_mahnung_mahnung— idempotente Migration laeuft beim ersten Setup-Aufruf nach dem Deploy. - Neuer Block "Versand & Belege" auf der Mahnungs-Karte:
- Erfassung Versanddatum + Versandweg (Brief/Einschreiben/DHL/DPD/Hermes/UPS/Fax/Mail/Persoenlich/Eigen).
- Optionale Sendungsnummer + Anbieter — Mahnung-Klasse liefert Deep-Link zur Sendungsverfolgung (DHL, Deutsche Post, DPD, Hermes, UPS).
- "Sendung verfolgen"-Button oeffnet die Provider-Seite mit eingesetzter Sendungsnummer.
- Beleg-Upload via Dolibarrs
formfile->showdocuments()— Dateien landen inDOL_DATA_ROOT/mahnung/<MAHN-Ref>/, voll integriert mit ECM/document.php. - Status springt automatisch auf
STATUS_VERSENDETsobald ein Versanddatum gesetzt wird (sofern vorher <= ERSTELLT). - Neue Methoden
Mahnung::setVersand(),Mahnung::trackingUrl(),Mahnung::defaultProviderForWeg(),Mahnung::getVersandwegLabel().
Vorschlagsliste — UX
- Kunden-Filter: rowid-Input ersetzt durch Dolibarr-Standard
select_company()(Ajax-Suche bzw. klassisches Dropdown, je nach Dolibarr-Konfiguration). Direkt-Links?search_socid=74bleiben funktional. - Neuer Filter "Mindestbetrag" (in EUR, Komma erlaubt).
- Neuer Filter "Kundentyp" (B2B / B2C).
- Neue Spalte "Kontakt" mit Telefon- und Mail-Direktlink-Icons.
Verzugszinsen-Neuberechnung
- Mahnungen im Status "Erstellt" (noch nicht versandt) werden beim Aufruf der card.php automatisch mit der aktuellen Stufen-Konfiguration neu berechnet. Aenderungen am Zinssatz-Override in den Einstellungen wirken sofort auf alle offenen Mahnungen.
- Toleranz 0,001 EUR um unnoetige DB-Writes zu vermeiden.
basiszins_snapshotwird ebenfalls aktualisiert.
Zinssatz-Override UX (Setup-Seite)
- Zinssatz-Override-Felder zeigen jetzt einen Placeholder mit dem Standard-Zinssatz (z.B. "6,27").
- Grauer Hilfetext neben dem Feld: "Leer = Standard (1,27 + 5,0 % = 6,27 %), 0 = keine Zinsen" — macht die Unterscheidung zwischen leer (Standard) und 0 (keine Zinsen) klar.
- Effektiver Zinssatz wird live aus den globalen Basiszins-/Aufschlag-Einstellungen berechnet.
Fixes
- Deutsche Post Tracking-URL korrigiert:
?piececode={nr}statt?form.sendungsnummer={nr}. showdocuments()Return-Wert wurde nicht geprinted — Sendebelege waren nach Upload unsichtbar.$upload_dirwar imscan_belege-Handler undefiniert (Variable wurde erst 90 Zeilen spaeter gesetzt) — eigene Pfadberechnung im Action-Block.pdftotextForm-Feed-Zeichen (\x0C) bei Bild-PDFs:trim()entfernt\x0Cnicht — explizite Zeichenliste noetig.- Kundenkarte: Tab "Mahnwesen" erschien doppelt, weil
complete_head_from_modules()pro Karte mehrfach (core + external + remove) feuert. Hook filtert jetzt aufmode=add+filterorigmodule=external.
[0.2.0] — 2026-05-10 — ODT-Template-System, Widget, Dokumentenmodelle
ODT-Template-System
- Abstrakte Basis-Klasse
ModelePDFMahnung extends CommonDocGenerator(core/modules/mahnung/modules_mahnung.php) - ODT-Generator
doc_generic_mahnung_odtmit Stufen-spezifischer Template-Auswahl (mahnung_stufe1/2/3.odt, Fallback mahnung.odt) - TCPDF-Generator
pdf_standard_mahnung(refactored ausmahnungpdf.class.php) mahnung.class.php:generateDocument()Methode viacommonGenerateDocument()- Template-Variablen: Mahnung, Rechnung, Kunde, Bankverbindung, Dolibarr-Standard
- ODT-Template-Upload auf Setup-Seite mit Benennungskonvention-Hinweis
Widget
box_mahnung_offen: Offene Kundenrechnungen mit Mahnstufe-Badge (basiert auf box_factures_imp)- Alle offenen Rechnungen (nicht nur überfällige), Status-Icon wie Original
- Farbige Mahnstufe-Badges (blau/orange/rot) mit Link zur Mahnung-Detailseite
- Strich (—) bei Rechnungen ohne Mahnung
Dokumentenliste auf card.php
- Generierte Dokumente zur Mahnung auflisten (aus Rechnungsordner)
- PDF-Vorschau (Lupe), Download-Button
- Modellauswahl-Dropdown bei mehreren aktiven Dokumentenmodellen
Setup-Erweiterungen
- Dokumentenmodell-Verwaltung (aktivieren/deaktivieren, Default setzen)
admin/templatevars.php: Referenzseite aller verfügbaren ODT-Template-Variablen- Link von Setup zur Variablen-Referenz
Modul-Descriptor
module_parts['models'] = 1- Neue Konstanten:
MAHNUNG_ADDON_PDF,MAHNUNG_ADDON_PDF_ODT_PATH - Document-Model-Registrierung in
init()(standard_mahnung + generic_mahnung_odt) - Widget
box_mahnung_offen@mahnungregistriert - Picto korrigiert:
fa-envelope-open-text(FA5-Free)
Bugfixes
- numero 500037 → 500038 (Kollision mit Eplan behoben)
verifCsrf()entfernt (existiert nicht in Dolibarr, CSRF vianewToken())f.statut→f.fk_statut(Dolibarr 22.x Spaltenname)actions_setmoduleoptions.inc.phpvorllxHeader()verschoben (ODT-Upload)- Widget:
require_once mahnung.class.phpan Dateianfang (Fatal Error bei Klassen-Konstanten in SQL)
Entfernt
class/mahnungpdf.class.php— Logik incore/modules/mahnung/doc/pdf_standard_mahnung.modules.php
[0.1.0] — 2026-05-07 — Erstveröffentlichung (Phase 1–10)
DB-Schema (Phase 1)
llx_mahnung_mahnung— Mahnvorgänge mit Stufe, Beträgen, Zinsen, Status, Snapshot des Basiszinses für Reproduzierbarkeitllx_mahnung_stufe— pro Stufe konfigurierbar: Frist, neue Frist, Gebühren B2C/B2B, optional Zinssatz-Override, Versandart, E-Mail-/PDF-Templates- 3 Default-Stufen werden bei Aktivierung idempotent eingefügt
Modul-Descriptor (Phase 1)
- numero
500038, familyfinancial, FA-Pictofa-envelope-open-text - Modul-Konstanten:
MAHNUNG_BASISZINS,MAHNUNG_AUFSCHLAG_B2C,MAHNUNG_AUFSCHLAG_B2B,MAHNUNG_PAUSCHALE_B2B,MAHNUNG_NTFY_TOPIC - Rechte:
read,write,send,delete,setup - Cron-Job
MahnungCronBuildVorschlag(täglich, default deaktiviert) - Linkes Menü unter „Rechnungen" (mainmenu=billing) mit Vorschlagsliste / Archiv
CRUD + Setup (Phase 2)
class/mahnung.class.php— CRUD, Status-Konstanten, Verzugszinsen-Berechnung nach BGB §288class/mahnungstufe.class.php— Stufen-Konfiguration, Override-Helfer für Zinsen/Gebührenadmin/setup.php— Stufen-Tabelle vollständig pflegbar, Konstanten persistent
Vorschlagsliste + Cron (Phase 3)
class/mahnungvorschlag.class.php— gemeinsamer Service: ermittelt pro überfälliger Rechnung die nächste vorgeschlagene Stufe, B2C/B2B-Erkennung viatva_intra, offener Betrag auspaiement_factureclass/mahnungcron.class.php— Cron sammelt Vorschläge, sendet Ntfy-Push (Topic aus Setup), schreibt zusätzlich GlobalNotify-Action wenn aktivclass/mahnungntfy.class.php— schmaler Ntfy-Push-Wrapperlist.php— Vorschlagsliste-UI mit Multi-Select, Filter nach Stufe / Verzugstagen / Kunde, Buttons „Mahnungen erzeugen" und „Sammelbrief"
PDF-Generator + Erstellen (Phase 4)
- TCPDF-basierter Generator (DIN-5008 Form A): Adressfenster, Bezugszeichenzeile, Tabelle, Gebührenblock, Verzugszinsen mit Snapshot-Zinssatz, neue Frist, Bankverbindungs-Footer
- PDFs landen in
documents/facture/{ref}/und erscheinen automatisch im Dokumente-Tab der Rechnung ajax/createmahnung.php— Bulk-Endpoint mit CSRF + Permission-Check, erzeugt Mahnung + PDF, behandelt §288 Abs. 5 Pauschale einmalig pro Rechnung
Hooks + Trigger (Phase 5)
core/triggers/interface_99_modMahnung_MahnungTriggers.class.php—BILL_PAYEDundPAYMENT_CUSTOMER_CREATEsetzen offene Mahnungen auf erledigtclass/actions_mahnung.class.php— Hook auf Rechnungs- und Kundenkarte: Tab „Mahnungen (n)" mit Badge, Button „Mahnung erstellen" wenn überfälligcard.php— Detailansicht eines Mahnvorgangs mit Storno-Aktion (formconfirm-Modal)
E-Mail + Sammelbrief (Phase 6)
ajax/sendmail.php— sendet Mahnung-PDF viaCMailFilean die Kunden-Mail; Subject/Body mit Platzhaltern aus Stufen-Konfigajax/sammelbrief.php— erzeugt Mahnungen für Auswahl, konkateniert ihre PDFs via TCPDI in eine Datei, liefert Download
Integrationen (Phase 7 + 8)
- GlobalNotify: Cron sendet zusätzlich
actionRequired-Notification ins Dolibarr-UI (wenn Modul aktiv) - Tab „Mahnungen" auf Kundenkarte (
thirdpartycard) zusätzlich zur Rechnungskarte
Pipeline (Phase 10)
.forgejo/workflows/deploy.yml— Deploy auf/mnt/appdata/firma/dolibarr-202509/modules/mahnungbei Push aufmainmit[deploy]oder Tagv*, Ntfy-Notify auf Topicvk-builds