diff --git a/CLAUDE.md b/CLAUDE.md
index 300e2a7..287735d 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -27,7 +27,10 @@ Dolibarr Custom-Modul: 3-stufiges Mahnwesen nach BGB §288 + Versand-Tracking +
- `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
+- **Zähler im Kopf**: Der Badge im Widget-Kopf zeigt die tatsächliche Gesamtzahl offener Rechnungen (verlinkt auf die Rechnungsliste). Titel-Lang-Key hat kein `(%s)` mehr — die Zahl steckt im Badge. Der Zähler wird erst NACH der Query gesetzt (wenn `$num` bekannt ist), damit die frühen Return-Pfade (fehlende Rechte / SQL-Fehler) ohne Zähler bleiben.
+- **Zeilenanzahl konfigurierbar** via Konstante `MAHNUNG_BOX_MAXLINES` (Admin-Select in `setup.php`: Alle/5/10/20/30/50, Default `0` = alle). Widget lädt IMMER alle offenen Rechnungen (für den korrekten Zähler), rendert aber nur `MAHNUNG_BOX_MAXLINES` Zeilen + eine `...`-Überlaufzeile. **Nicht** auf das von Dolibarr übergebene `$max` verlassen — das kommt aus `MAIN_SIZE_SHORTLIST_LIMIT` (Default 5) und gilt global für ALLE Home-Boxen. (KB #598)
- **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.
+- **Summenzeile Netto+Brutto**: Betragszelle der `liste_total`-Zeile zeigt zweizeilig Netto (`SUM(f.total_ht)`) und Brutto (`SUM(f.total_ttc)`). Brutto kommt direkt aus `f.total_ttc` der Rechnung, NICHT aus Netto × Steuersatz hochgerechnet — sonst wären Reverse-Charge §13b, Steuerbefreiung und Kleinunternehmer §19 UStG falsch. Lang-Keys `MahnungBoxNetto`/`MahnungBoxBrutto`.
## 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)
diff --git a/README.md b/README.md
index 763966b..cb62256 100644
--- a/README.md
+++ b/README.md
@@ -25,7 +25,7 @@ Version **0.2.0 + Phasen 1-6 (Unreleased)** — UX-Vorschlagsliste, Versand-Erfa
| E-Mail-Versand mit PDF-Anhang | `CMailFile`, Subject/Body mit Platzhaltern |
| Auto-Erledigung bei Zahlungseingang | Trigger `BILL_PAYED` + `PAYMENT_CUSTOMER_CREATE` |
| Tab "Mahnungen (n)" auf Rechnungs- + Kundenkarte | Hook `completeTabsHead` (filtert auf add/external) |
-| Widget: Offene Rechnungen mit Mahnstufe | `box_mahnung_offen` |
+| Widget: Offene Rechnungen mit Mahnstufe (Zähler im Kopf, Zeilenanzahl einstellbar, Summenzeile Netto+Brutto) | `box_mahnung_offen`, Konstante `MAHNUNG_BOX_MAXLINES` |
| **Vorschlagsliste-Filter** (Mahnstufe, Tage Verzug, Mindestbetrag, Kundentyp B2B/B2C, Kunden-Select2 mit Ajax) | `list.php` — Kundentyp-Wechsel filtert das Kunden-Dropdown sofort per Auto-Submit |
| **Kontakt-Spalte** in Vorschlagsliste (Telefon-/Mail-Icons mit Direktlink) | tel:/mailto: aus `llx_societe.phone/email` |
| **Versand-Erfassung** pro Mahnung (Datum, Versandweg, Sendungsnummer, Tracking-Provider) | `card.php` Versand-Block; setzt automatisch Status auf VERSENDET |
@@ -123,6 +123,7 @@ Vollständige Liste: **Setup → Mahnwesen → Verfügbare Template-Variablen**
| `MAHNUNG_ADDON_PDF_ODT_PATH` | `DOL_DATA_ROOT/doctemplates/mahnung` | ODT-Template-Verzeichnis |
| `MAHNUNG_NTFY_TOPIC` | `vk-builds` | Ntfy-Topic für Vorschlags-Push |
| `MAHNUNG_VERSAND_REMINDER_DAYS` | `2` | Schwelle (Tage) für Versand-Reminder Ntfy-Push |
+| `MAHNUNG_BOX_MAXLINES` | `0` | Widget: max. angezeigte offene Rechnungen (`0` = alle) |
## Datenbank
diff --git a/core/boxes/box_mahnung_offen.php b/core/boxes/box_mahnung_offen.php
index a67032d..9ff7c55 100644
--- a/core/boxes/box_mahnung_offen.php
+++ b/core/boxes/box_mahnung_offen.php
@@ -240,8 +240,11 @@ class box_mahnung_offen extends ModeleBoxes
}
if ($num > 0) {
- // Summe (wie Original: separate Query ohne LIMIT)
- $sql2 = "SELECT SUM(f.total_ht) as total_ht";
+ // Summe (wie Original: separate Query ohne LIMIT).
+ // Brutto direkt aus f.total_ttc der Rechnung, NICHT aus total_ht
+ // hochgerechnet — so bleiben Sonderregelungen (Reverse-Charge §13b,
+ // Steuerbefreiung, Kleinunternehmer §19 UStG) korrekt.
+ $sql2 = "SELECT SUM(f.total_ht) as total_ht, SUM(f.total_ttc) as total_ttc";
$sql2 .= " FROM ".MAIN_DB_PREFIX."facture as f";
$sql2 .= " INNER JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid = f.fk_soc";
$sql2 .= " WHERE f.entity IN (".getEntity('invoice').")";
@@ -251,9 +254,11 @@ class box_mahnung_offen extends ModeleBoxes
}
$resTotal = $this->db->query($sql2);
$totalHt = 0;
+ $totalTtc = 0;
if ($resTotal) {
$objTotal = $this->db->fetch_object($resTotal);
$totalHt = (float) $objTotal->total_ht;
+ $totalTtc = (float) $objTotal->total_ttc;
$this->db->free($resTotal);
}
@@ -266,9 +271,13 @@ class box_mahnung_offen extends ModeleBoxes
'td' => 'class="liste_total"',
'text' => ' ',
);
+ // Netto (wie bisher) + Brutto darunter, Brutto aus f.total_ttc der
+ // Rechnung (Sonderregelungen wie §13b/§19 UStG bleiben korrekt).
$this->info_box_contents[$line][] = array(
'td' => 'class="nowraponall right liste_total"',
- 'text' => price($totalHt, 0, $langs, 0, -1, -1, $conf->currency),
+ 'text' => ''.$langs->trans("MahnungBoxNetto").''.price($totalHt, 0, $langs, 0, -1, -1, $conf->currency)
+ .'
'.$langs->trans("MahnungBoxBrutto").''.price($totalTtc, 0, $langs, 0, -1, -1, $conf->currency),
+ 'asis' => 1,
);
$this->info_box_contents[$line][] = array(
'td' => 'class="liste_total"',
diff --git a/langs/de_DE/mahnung.lang b/langs/de_DE/mahnung.lang
index 8fe2c96..aa0c928 100644
--- a/langs/de_DE/mahnung.lang
+++ b/langs/de_DE/mahnung.lang
@@ -209,6 +209,8 @@ MahnungBoxKeineOffenenRechnungen = Keine offenen Kundenrechnungen
MahnungBoxMaxLines = Widget: angezeigte offene Rechnungen
MahnungBoxMaxLinesAll = Alle
MahnungBoxMaxLinesHelp = Wie viele offene Rechnungen das Home-Widget maximal anzeigt. „Alle" = ohne Begrenzung. Der Zähler im Widget-Kopf zeigt immer die tatsächliche Gesamtzahl.
+MahnungBoxNetto = Netto
+MahnungBoxBrutto = Brutto
#
# Dokumentenmodelle
diff --git a/langs/en_US/mahnung.lang b/langs/en_US/mahnung.lang
index 30cb842..0ed1265 100644
--- a/langs/en_US/mahnung.lang
+++ b/langs/en_US/mahnung.lang
@@ -334,6 +334,8 @@ MahnungBoxKeineOffenenRechnungen = No open customer invoices
MahnungBoxMaxLines = Widget: displayed open invoices
MahnungBoxMaxLinesAll = All
MahnungBoxMaxLinesHelp = How many open invoices the home widget shows at most. "All" = no limit. The counter in the widget header always shows the real total.
+MahnungBoxNetto = Net
+MahnungBoxBrutto = Gross
MahnungVorschlagStufeNichtKonfiguriert = Stage 1 not configured
MahnungVorschlagFristNichtErreicht = Stage 1 deadline (%s days) not yet reached (overdue %s days)
MahnungVorschlagAlleStufenAusgeschoepft = All dunning stages exhausted (last stage %s)