mahnung/CLAUDE.md
Eduard Wisch 73e377dc01
All checks were successful
Deploy mahnung / deploy (push) Successful in 12s
fix(box): Widget bleibt sichtbar wenn keine offenen Rechnungen vorhanden [deploy]
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

4.4 KiB

CLAUDE.md — Mahnung-Modul

Projekt

Dolibarr Custom-Modul: 3-stufiges Mahnwesen nach BGB §288 + Versand-Tracking + Forderungsausfall-Workflow.

Technisches

  • numero: 500038 (NICHT ändern — 500037 ist Eplan)
  • Deploy: nur via Pipeline ([deploy] in Commit-Message), NIEMALS manuell kopieren
  • Prod-Pfad: /mnt/appdata/firma/dolibarr-202509/modules/mahnung/
  • Lokal: Symlink /var/www/dolibarr/custom/mahnung → repo/
  • Test-DB: dolibarr_test auf 192.168.155.11
  • Forgejo-Repo: data/mahnung (NICHT data-it/ — historisch, soll bleiben)

Schema-Migration

  • modMahnung::migrateVersandFelder() läuft automatisch beim Setup-Page-Aufruf
  • Idempotent via SHOW COLUMNS LIKE → fehlende Spalten via ALTER TABLE ADD COLUMN
  • Default-Tracking-Patterns werden via MahnungTrackingPattern::seedDefaults() geseedet (Check: COUNT(*) > 0 → skip)
  • Nach Deploy: User muss Setup-Page einmal aufrufen, sonst fehlen die neuen Spalten

Dokumentenmodell-System

  • commonGenerateDocument() fügt automatisch doc_/pdf_ Prefix hinzu
  • DB-Einträge in llx_document_model.nom OHNE Prefix speichern
  • actions_setmoduleoptions.inc.php MUSS vor llxHeader() stehen (Upload)
  • ODT-Templates: mahnung_stufe1.odt, mahnung_stufe2.odt, mahnung_stufe3.odt, mahnung.odt (Fallback)

Widget

  • box_mahnung_offen basiert 1:1 auf box_factures_imp.php (Standard-Widget)
  • Zeigt ALLE offenen Rechnungen, nicht nur überfällige
  • Mahnstufe-Badge nur wenn Mahnung existiert, sonst Strich

Hooks-Stolperfallen

  • completeTabsHead wird bei jedem Aufruf von complete_head_from_modules() getriggert — pro Karte mehrfach (core + external + remove). Filter auf mode=add + filterorigmodule=external, sonst doppelter Tab. (KB #601)
  • Hook-Kontexte: invoicecard, thirdpartycard, ordercard — letztere für Bonitäts-Warnings.

Filter-Syntax-Stolperfallen

  • $form->select_company($selected, $htmlname, $filter, ...): der $filter-Parameter erwartet USC-Syntax (feld:operator:wert), NICHT plain SQL. Beispiel B2C: (s.tva_intra:is:NULL) OR (s.tva_intra:=:''). Sonst SQL-Syntax-Error + 500. (KB #602)
  • search_socid=-1 wird von select_company als "nichts ausgewählt" geliefert → im Filter-Check > 0 statt !empty() nutzen.

Pipeline-Stolperfallen

  • ${{ github.event.head_commit.message }} NIE direkt in run:-Skript interpolieren — bei Sonderzeichen (Klammern, Backticks) bricht Bash. Immer via env: durchreichen. (KB #603)
  • [deploy]-Tag im Commit nötig, sonst kein Auto-Deploy.

Verzugszinsen-Override

  • zinssatz_b2c_uebersteuern / zinssatz_b2b_uebersteuern in llx_mahnung_stufe: NULL = Standard (Basiszins + Aufschlag), 0 = keine Zinsen, Wert = fester Prozentsatz
  • Nicht-versandte Mahnungen (Status ≤ ERSTELLT) werden beim card.php-Aufruf automatisch neu berechnet
  • Setup-Seite zeigt Placeholder mit Standard-Zinssatz + Hilfetext

Versand & Bonität (Phase 6)

  • Versand-Felder: date_versand, versandweg, tracking_nr, tracking_provider an llx_mahnung_mahnung
  • Tracking-URLs aus DB (llx_mahnung_trackingpattern) via MahnungTrackingPattern::urlFor(), Fallback: Mahnung::trackingUrl() (hardcoded)
  • Beleg-Upload: formfile->showdocuments('mahnung', $ref, $filedir, ...)$conf->mahnung->dir_output wird von Dolibarr automatisch gesetzt (KB #605), kein Custom-Setup nötig
  • Beleg-Scan: pdftotext + ocrmypdf (OCR-Fallback für Bild-PDFs) im 90-Dolibarr-Prod-Custom-Container; Pattern-Match via MahnungTrackingPattern::detectFromText()
  • pdftotext gibt \x0C (Form-Feed) bei Bild-PDFs zurück — trim() mit expliziter Zeichenliste " \t\n\r\0\x0B\x0C" nötig
  • "Übernehmen" setzt tracking_nr + tracking_provider + date_versand + versandweg automatisch (kein extra Speichern)
  • Uneinbringlich-Klassifikation: Facture::setCanceled($user, CommonInvoice::CLOSECODE_BADDEBT, $note) → setzt fk_statut=3 + close_code='badcustomer' (KB #606)
  • Steuer-Modul kompatibel: EÜR ignoriert (liest nur llx_paiement), UStVA filtert fk_statut IN (1,2) automatisch (KB #607)

Dolibarr-Versionshinweise

  • f.fk_statut statt f.statut (seit Dolibarr 22.x)
  • verifCsrf() existiert nicht — CSRF via newToken() + GETPOST('token')
  • dol_mkdir() gibt 0 zurück wenn Verzeichnis bereits existiert (nicht false)
  • dol_dir_list() gibt fullname zurück (nicht fullpath)
  • $form->formconfirm() unterstützt textarea-Feld via $formquestion-Array (KB #609)