mahnung/CLAUDE.md
Eduard Wisch 41765cc5df
All checks were successful
Deploy mahnung / deploy (push) Successful in 13s
docs(claude): Widget-Empty-State-Pflicht in CLAUDE.md dokumentiert [deploy]
Verweis auf KB #682: bei $num==0 muss Platzhalter-Zeile in
info_box_contents gesetzt werden, sonst verschwindet das Widget.

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

64 lines
4.7 KiB
Markdown

# 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
- **Empty-State Pflicht**: bei `$num == 0` Platzhalter-Zeile in `info_box_contents` einfügen — sonst rendert `ModeleBoxes::showBox()` gar nichts und das Widget verschwindet komplett (auch nach neuen Rechnungen sieht der User es nicht zurückkommen). Siehe KB #682.
## 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)