Commit graph

14 commits

Author SHA1 Message Date
73e377dc01 fix(box): Widget bleibt sichtbar wenn keine offenen Rechnungen vorhanden [deploy]
All checks were successful
Deploy mahnung / deploy (push) Successful in 12s
Wenn alle Rechnungen bezahlt sind, blieb info_box_contents leer und
ModeleBoxes::showBox renderte keinen Widget-Rahmen mehr. Widget kam
auch nach neuen Rechnungen nicht zurück. Fix: bei 0 Treffern eine
Platzhalter-Zeile "Keine offenen Kundenrechnungen" einfügen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 07:38:09 +02:00
ce525cee18 Setup: Zinssatz-Override zeigt effektiven Satz + Hilfetext [deploy]
All checks were successful
Deploy mahnung / deploy (push) Successful in 13s
- Eingabefelder zeigen Placeholder mit Standard-Zinssatz (Basiszins + Aufschlag)
- Grauer Hilfetext: "Leer = Standard (1,27 + 5,0 % = 6,27 %), 0 = keine Zinsen"
- Effektiver Zinssatz wird live aus globalen Einstellungen berechnet
- Prod-DB: Stufe 1 Override von 0.0000 auf NULL korrigiert (direkt in DB)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-13 17:28:11 +02:00
7196f2594c OCR für Bild-PDFs beim Belege-Scannen + Feedback-Meldung [deploy]
All checks were successful
Deploy mahnung / deploy (push) Successful in 13s
Wenn pdftotext keinen Text findet, wird ocrmypdf (Tesseract OCR)
auf das PDF angewendet bevor erneut nach Sendungsnummern gesucht wird.
Bei leerem Ergebnis erscheint jetzt eine Hinweismeldung statt
stummem Seiten-Reload.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-13 16:40:33 +02:00
10cf41a687 i18n: Alle Texte über $langs->trans() — ~100 neue Sprachschlüssel de_DE + en_US [deploy]
All checks were successful
Deploy mahnung / deploy (push) Successful in 14s
Umlaute in allen lang-Dateien korrigiert. Alle hardcodierten deutschen Strings
in 22 PHP-Dateien durch $langs->trans('Key') ersetzt. Neue Schlüssel für
Cron-Meldungen, Dokument-Aktionen, Bonität, Vorschlag-Status, Template-Vars u.a.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-13 16:25:50 +02:00
80d92042bc Fix: Menü-Navigation, GlobalNotify-URL, Dokument-Upload + -Löschen [deploy]
All checks were successful
Deploy mahnung / deploy (push) Successful in 13s
- GlobalNotify-URL: relativer Pfad statt absoluter (DOL_MAIN_URL_ROOT ohne
  Protokoll führte zu kaputten Seiten-URLs im Browser); buildAbsoluteUrl()
  prüft jetzt auf fehlendes http(s)://
- Menü-Navigation: mainmenu=billing&leftmenu=mahnung in allen list.php-Links
  ergänzt (createmahnung.php Redirect, Filter-Form, Hook-Links, Cron-Pfade)
- card.php Dokumente: Lupe direkt neben Dateinamen, Löschen-Button pro Datei
- card.php Upload: actions_linkedfiles.inc.php eingebunden (vor llxHeader),
  upload_dir korrekt gesetzt — showdocuments() zeigt jetzt Upload-Formular
- Redundante Variablen-Definitionen (mahnungSafeRef, files.lib require) entfernt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-13 15:36:58 +02:00
3a49c67fbd Bonitaets-Workflow: Warnbox Kundenkarte + Uneinbringlich-Button Stufe 3 [deploy]
All checks were successful
Deploy mahnung / deploy (push) Successful in 13s
Bonitaets-Anzeige:
- Hook tabContentViewThirdparty rendert prominente rote Warnbox auf der
  Kundenkarte wenn fk_statut=3 + close_code=badcustomer existiert. Zeigt
  Anzahl, Gesamtsumme, Datum letzter Abschreibung + Link zur Detail-Liste.
- Hook formObjectOptions zeigt kompakte Warn-Zeile auf ordercard und
  invoicecard wenn der Kunde Forderungsausfaelle hat.
- ordercard zum module_parts.hooks.data ergaenzt.

Uneinbringlich-Button:
- Auf Mahnung-Karten der Stufe 3, Status >= ERSTELLT, nicht storniert,
  Rechnung noch nicht abandoned.
- Bestaetigungs-Dialog mit Begruendungs-Textfeld (Default-Text setzt
  das aktuelle Datum ein).
- Ruft Facture::setCanceled mit CommonInvoice::CLOSECODE_BADDEBT.
- Mahnung wird storniert + Begruendung in note_private festgehalten.

Steuer-Modul kompatibel: EÜR liest nur llx_paiement (keine Zahlung =
keine Einnahme), UStVA filtert fk_statut IN (1,2) — abandoned Rechnungen
werden automatisch korrekt ausgeschlossen. Bei Ist-Versteuerung damit
buchhalterisch sauber, kein manueller Eingriff noetig.

Lang-Keys: 16 neu (de_DE + en_US) fuer Bonitaets-Box + Uneinbringlich-Workflow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 13:17:44 +02:00
660e91e65d Versand-Reminder Cron + Ntfy-Push fuer unversendete Mahnungen [deploy]
Some checks failed
Deploy mahnung / deploy (push) Failing after 4s
Neuer Cron-Job MahnungCronVersandReminder (taeglich):
- Sucht in llx_mahnung_mahnung Status=ERSTELLT (1) + date_versand IS NULL
  + datec < NOW() - INTERVAL N DAY.
- N steht in der Konstante MAHNUNG_VERSAND_REMINDER_DAYS (Default 2).
- Bei Treffern: Ntfy-Push (Topic MAHNUNG_NTFY_TOPIC) mit Titel + Liste
  der bis zu 8 Mahnungen ("MAHN2026-0042 (Stufe 2, 3 Tage alt) — Kunde").
- Optional GlobalNotify-Badge "mahnung_versand" wenn GlobalNotify aktiv.

Modul-Descriptor:
- Cronjobs-Array um Reminder ergaenzt (frequency 1d, priority 55, status 1).

Lang-Keys: 2x (de_DE + en_US) fuer Cron-Label + Description.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 12:08:44 +02:00
216c185fb7 Beleg-Scan: Sendungsnummer aus hochgeladenen PDFs erkennen [deploy]
All checks were successful
Deploy mahnung / deploy (push) Successful in 14s
Neuer Button "Belege scannen" im Versand-Block der Mahnungs-Karte:
- Action scan_belege durchlaeuft alle Files in DOL_DATA_ROOT/mahnung/<MAHN-Ref>/
- PDFs werden via pdftotext (CLI, mit Verfuegbarkeits-Check) extrahiert
- txt/html werden direkt eingelesen (HTML mit strip_tags)
- Pro Datei wird MahnungTrackingPattern::detectFromText() angewendet —
  matched gegen alle aktiven Patterns nach priority DESC
- Treffer landen in $_SESSION als Vorschlag (file, provider, nr, url, label)

UX:
- Vorschlags-Banner mit gruener Linke ueber dem Beleg-Bereich
- Pro Vorschlag: Datei-Icon, Pattern-Label, Sendungsnummer als <code>,
  externer Link zur Sendungsverfolgung, "Uebernehmen"-Button
- "Uebernehmen" (action=apply_tracking) speichert tracking_nr +
  tracking_provider an der Mahnung und leert Session
- "Verwerfen" (action=dismiss_tracking) entfernt nur Session-Eintrag

Fallback:
- Wenn pdftotext nicht im Container verfuegbar: Warnmeldung im UI,
  txt/html werden trotzdem verarbeitet.

OCR fuer Bilder (PNG/JPG) bewusst noch nicht enthalten — separater Schritt
mit Container-Anpassung (Tesseract) wenn gewuenscht.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 12:07:09 +02:00
56954d68f3 Konfigurierbare Tracking-Patterns mit Live-Vorschau [deploy]
Some checks failed
Deploy mahnung / deploy (push) Failing after 4s
Datenmodell:
- Neue Tabelle llx_mahnung_trackingpattern (provider, label, regex,
  url_template, priority, active). Auto-Anlage + Default-Seed im setup.php
  und in modMahnung::init() — idempotent.

Default-Patterns (priority hoeher = spezifischer, zuerst gepruef):
- DHL Paket 20-stellig (90), DPAG Einschreiben RR...DE (85), UPS 1Z... (80),
- DHL 11-stellig Online-Frankierung (30), Hermes 14-stellig (25), DPD (20).

Setup-Seite admin/tracking_patterns.php:
- CRUD (Anlegen/Bearbeiten/Aktivieren-Deaktivieren/Loeschen)
- Live-Vorschau: Regex + Beispieltext + URL-Template werden waehrend
  des Tippens (debounce 300ms) via ajax/regex_preview.php ausgewertet.
  UI zeigt: Regex-Syntax-Status, gefundene Sendungsnummer, vollstaendige
  Tracking-URL (anklickbar).
- Validierung: Delimiter / # ~ Whitelist, https://-Pflicht, {nr}-Platzhalter,
  max 255 Zeichen Regex.

AJAX-Endpoint ajax/regex_preview.php:
- ReDoS-Schutz: max 10 KB Sample, pcre.backtrack_limit=100k.
- POST-only (mit Setup-Recht), JSON-Response.

card.php:
- tracking-URL kommt jetzt aus MahnungTrackingPattern::urlFor() (DB-Lookup
  nach Provider) statt hardcoded Mahnung::trackingUrl() — letztere bleibt
  als Fallback.

Setup-Seite: neuer Button "Tracking-Muster (Regex)" oben rechts.

Lang-Keys: 23 neue (de_DE + en_US) fuer Pattern-CRUD + Live-Vorschau.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 12:04:42 +02:00
bb610a7594 Versand-Erfassung + Sendebeleg-Upload pro Mahnung [deploy]
Some checks failed
Deploy mahnung / deploy (push) Failing after 4s
Datenmodell (llx_mahnung_mahnung):
- Neue Spalten: date_versand, versandweg, tracking_nr, tracking_provider.
- Idempotente Migration via modMahnung::migrateVersandFelder() — laeuft
  automatisch beim ersten Setup-Aufruf nach dem Deploy (SHOW COLUMNS-Check,
  fehlende Spalten werden per ALTER TABLE ergaenzt).

Mahnung-Klasse:
- setVersand() — speichert Datum + Weg + optional Tracking + setzt Status
  automatisch auf VERSENDET wenn vorher <= ERSTELLT.
- defaultProviderForWeg() — Mapping versandweg → tracking_provider
  (dhl→dhl, einschreiben→dpag, dpd→dpd, hermes→hermes, ups→ups).
- trackingUrl() — Deep-Link-Helper fuer DHL, Deutsche Post, DPD, Hermes, UPS.
- getVersandwegLabel() — lokalisiertes Label aus versandweg-Slug.
- fetch/update um die neuen Felder erweitert.

card.php:
- Neuer Block "Versand & Belege" nach den generierten Dokumenten.
- Erfassungs-Formular: Datepicker + Versandweg-Dropdown (10 Optionen) +
  Sendungsnummer + Provider-Override.
- Anzeige nach Erfassung mit "Sendung verfolgen"-Button zum Provider.
- "Versand bearbeiten" und "Versand zuruecksetzen" Buttons.
- Beleg-Upload via formfile->showdocuments('mahnung', ...) — Dateien
  landen in DOL_DATA_ROOT/mahnung/<MAHN-Ref>/.
- Modulpart "mahnung" funktioniert ohne Custom-Setup: Dolibarr's
  conf.class.php:744 setzt $conf->mahnung->dir_output automatisch.

Lang-Keys: 22 neue (de_DE + en_US) fuer Versand-Block.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 11:59:08 +02:00
8e5e26c162 Vorschlagsliste: Kunden-Select2 + Mindestbetrag + Kundentyp-Filter + Kontakt-Spalte [deploy]
All checks were successful
Deploy mahnung / deploy (push) Successful in 14s
Filter-Zeile:
- Kunden-Filter "rowid"-Input ersetzt durch $form->select_company()
  (Ajax-Suche falls COMPANY_USE_SEARCH_TO_SELECT gesetzt, sonst klassisches
  Dropdown). Direkt-Links ?search_socid=74 von der Kundenkarte bleiben
  weiterhin funktional (htmlname=search_socid, Backward-kompatibel).
- Neuer Filter "Mindestbetrag" (EUR, Komma zugelassen).
- Neuer Filter "Kundentyp" (alle / B2B / B2C).

Tabelle:
- Neue Spalte "Kontakt" mit Telefon- und Mail-Direktlink-Icons (tel: / mailto:).
- Spalte erscheint sowohl in der Vorschlags- als auch in der Uebersprungen-Tabelle.

MahnungVorschlag::getVorschlaege() + buildAlleVorschlaege():
- SELECT erweitert um s.phone + s.email; werden als soc_phone/soc_email
  pro Eintrag mitgegeben.
- Neue PHP-side Filter min_betrag und kundentyp.

Lang-Keys: MahnungKontakt (de_DE + en_US).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 11:49:31 +02:00
993ac50c0c Feature: ODT-Template-System, Widget, Dokumentenliste, Templatevariablen-Referenz [deploy]
All checks were successful
Deploy mahnung / deploy (push) Successful in 13s
- ODT-Template-System: Pro Mahnstufe eigenes Template (mahnung_stufe1/2/3.odt),
  Fallback auf generisches Template; Basis-Klasse ModelePDFMahnung,
  pdf_standard_mahnung (TCPDF refactored), doc_generic_mahnung_odt (ODTPHP)
- Widget box_mahnung_offen: Überfällige Rechnungen mit Mahnstufe-Badge (blau/orange/rot)
- card.php: Dokumentenliste mit Lupe (PDF-Vorschau), Download, Modellauswahl
- admin/templatevars.php: Referenzseite aller verfügbaren ODT-Variablen
- admin/setup.php: Dokumentenmodell-Verwaltung, Upload-Bereich mit Benennungskonvention
- mahnung.class.php: generateDocument() + socid-Alias für commonGenerateDocument()
- modMahnung.class.php: models=1, MAHNUNG_ADDON_PDF/ODT_PATH-Konstanten,
  document_model-Registrierung in init(), Widget registriert
- mahnungpdf.class.php entfernt, Logik in pdf_standard_mahnung.modules.php

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-10 18:28:24 +02:00
d889fb25a5 Archiv zeigt Kundennamen, Vorschlagsliste mit Skip-Diagnose [deploy]
All checks were successful
Deploy mahnung / deploy (push) Successful in 13s
- list.php Archiv: Rechnungs-Ref + Kunden-Name per Bulk-Query (statt rowid).
  Mahnung-Ref klickt zur Detailansicht (card.php).
- MahnungVorschlag: neue buildAlleVorschlaege()/getUebersprungeneRechnungen()
  liefern auch ueberfaellige Rechnungen ohne aktuellen Vorschlag inkl. skip_reason
  (Wartefrist laeuft, Stufen ausgeschoepft, Frist Stufe 1 nicht erreicht, ...).
- list.php Vorschlagsliste: zweite Tabelle "Aktuell uebersprungen" mit Grund.
  Erklaert warum ueberfaellige Rechnungen nicht in der Vorschlagsliste auftauchen.
- Lang-Keys MahnungUebersprungen/Hint/SkipGrund (DE+EN).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 12:16:20 +02:00
d1db85322b Initiales Release: Mahnung-Modul v0.1.0 [deploy]
All checks were successful
Deploy mahnung / deploy (push) Successful in 14s
Vollstaendiges 3-stufiges Mahnwesen nach BGB §288:
- SQL-Schema (llx_mahnung_mahnung, llx_mahnung_stufe)
- CRUD-Klassen (Mahnung, MahnungStufe, MahnungVorschlag)
- TCPDF DIN-5008 PDF-Generierung
- Verzugszinsberechnung B2C/B2B + §288 Abs.5 Pauschale
- Trigger: offene Mahnungen bei Zahlungseingang schliessen
- Hook: Tab + Button auf Rechnungs-/Kundenkarte
- Cron: taegl. Vorschlagsliste + Ntfy-Push
- Deploy-Pipeline (.forgejo/workflows/deploy.yml)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 12:09:37 +02:00