From 8e5e26c16291598df5fd1ef0e0f370d23639da98 Mon Sep 17 00:00:00 2001 From: Eduard Wisch Date: Mon, 11 May 2026 11:49:31 +0200 Subject: [PATCH] Vorschlagsliste: Kunden-Select2 + Mindestbetrag + Kundentyp-Filter + Kontakt-Spalte [deploy] 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) --- CHANGELOG.md | 6 +++ class/mahnungvorschlag.class.php | 21 +++++++-- langs/de_DE/mahnung.lang | 1 + langs/en_US/mahnung.lang | 1 + list.php | 80 ++++++++++++++++++++++++++++++-- 5 files changed, 103 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e90a7e3..0c40186 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## [Unreleased] +### 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=74` bleiben funktional. +- Neuer Filter "Mindestbetrag" (in EUR, Komma erlaubt). +- Neuer Filter "Kundentyp" (B2B / B2C). +- Neue Spalte "Kontakt" mit Telefon- und Mail-Direktlink-Icons. + ### Fixes - Kundenkarte: Tab "Mahnwesen" erschien doppelt, weil `complete_head_from_modules()` pro Karte mehrfach (core + external + remove) feuert. Hook filtert jetzt auf `mode=add` + `filterorigmodule=external`. diff --git a/class/mahnungvorschlag.class.php b/class/mahnungvorschlag.class.php index 7b703de..b7e9023 100644 --- a/class/mahnungvorschlag.class.php +++ b/class/mahnungvorschlag.class.php @@ -52,7 +52,8 @@ class MahnungVorschlag * vorgeschlagene_stufe (int 1..3), * vorgeschlagene_stufe_label (string) * - * @param array $filter Optional: 'soc_id', 'min_tage_verzug', 'max_tage_verzug', 'stufe' + * @param array $filter Optional: 'soc_id', 'min_tage_verzug', 'max_tage_verzug', 'stufe', + * 'min_betrag' (float), 'kundentyp' ('B2B'|'B2C') * @return array */ public function getVorschlaege(array $filter = array()) @@ -65,7 +66,7 @@ class MahnungVorschlag $today = dol_now(); $sql = "SELECT f.rowid AS facture_id, f.ref AS facture_ref, f.date_lim_reglement,"; $sql .= " f.total_ttc, f.fk_soc, f.paye, f.fk_statut,"; - $sql .= " s.nom AS soc_nom, s.tva_intra"; + $sql .= " s.nom AS soc_nom, s.tva_intra, s.phone AS soc_phone, s.email AS soc_email"; $sql .= " FROM ".MAIN_DB_PREFIX."facture as f"; $sql .= " INNER JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid = f.fk_soc"; $sql .= " WHERE f.entity = ".((int) $this->entity); @@ -102,6 +103,12 @@ class MahnungVorschlag if (isset($filter['stufe']) && $filter['stufe'] !== '' && (int) $row['vorgeschlagene_stufe'] !== (int) $filter['stufe']) { continue; } + if (isset($filter['min_betrag']) && (float) $row['betrag_offen'] < (float) $filter['min_betrag']) { + continue; + } + if (!empty($filter['kundentyp']) && $row['kundentyp'] !== $filter['kundentyp']) { + continue; + } $result[] = $row; } $this->db->free($resql); @@ -145,7 +152,7 @@ class MahnungVorschlag $today = dol_now(); $sql = "SELECT f.rowid AS facture_id, f.ref AS facture_ref, f.date_lim_reglement,"; $sql .= " f.total_ttc, f.fk_soc, f.paye, f.fk_statut,"; - $sql .= " s.nom AS soc_nom, s.tva_intra"; + $sql .= " s.nom AS soc_nom, s.tva_intra, s.phone AS soc_phone, s.email AS soc_email"; $sql .= " FROM ".MAIN_DB_PREFIX."facture as f"; $sql .= " INNER JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid = f.fk_soc"; $sql .= " WHERE f.entity = ".((int) $this->entity); @@ -176,6 +183,12 @@ class MahnungVorschlag if (isset($filter['min_tage_verzug']) && $row['tage_verzug'] < (int) $filter['min_tage_verzug']) { continue; } + if (isset($filter['min_betrag']) && (float) $row['betrag_offen'] < (float) $filter['min_betrag']) { + continue; + } + if (!empty($filter['kundentyp']) && $row['kundentyp'] !== $filter['kundentyp']) { + continue; + } $result[] = $row; } $this->db->free($resql); @@ -256,6 +269,8 @@ class MahnungVorschlag 'soc_id' => (int) $factureObj->fk_soc, 'soc_nom' => $factureObj->soc_nom, 'soc_tva_intra' => $factureObj->tva_intra, + 'soc_phone' => $factureObj->soc_phone ?? '', + 'soc_email' => $factureObj->soc_email ?? '', 'kundentyp' => $kundentyp, 'tage_verzug' => $tageVerzug, 'betrag_offen' => $betragOffen, diff --git a/langs/de_DE/mahnung.lang b/langs/de_DE/mahnung.lang index bdfc45a..0052b0e 100644 --- a/langs/de_DE/mahnung.lang +++ b/langs/de_DE/mahnung.lang @@ -65,6 +65,7 @@ MahnungVersandNone = Kein Versand MahnungRef = Mahnung-Nr. MahnungRechnung = Rechnung MahnungKunde = Kunde +MahnungKontakt = Kontakt MahnungKundentyp = Typ MahnungKundentypB2C = Privat (B2C) MahnungKundentypB2B = Geschaeftlich (B2B) diff --git a/langs/en_US/mahnung.lang b/langs/en_US/mahnung.lang index 5acdbfb..fa45a61 100644 --- a/langs/en_US/mahnung.lang +++ b/langs/en_US/mahnung.lang @@ -65,6 +65,7 @@ MahnungVersandNone = No dispatch MahnungRef = Dunning ref. MahnungRechnung = Invoice MahnungKunde = Customer +MahnungKontakt = Contact MahnungKundentyp = Type MahnungKundentypB2C = Private (B2C) MahnungKundentypB2B = Business (B2B) diff --git a/list.php b/list.php index 70bc613..6c3fcd2 100644 --- a/list.php +++ b/list.php @@ -41,6 +41,7 @@ if (!$res) { require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/class/mahnung.class.php'; require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/class/mahnungstufe.class.php'; require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/class/mahnungvorschlag.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php'; global $langs, $user, $db; $langs->loadLangs(array('mahnung@mahnung', 'companies', 'bills')); @@ -49,6 +50,8 @@ if (!$user->hasRight('mahnung', 'read')) { accessforbidden(); } +$form = new Form($db); + $mode = GETPOST('mode', 'aZ09'); if ($mode !== 'archiv') { $mode = 'vorschlag'; @@ -67,6 +70,17 @@ $filter_socid = GETPOST('search_socid', 'int'); if (!empty($filter_socid)) { $filter['soc_id'] = (int) $filter_socid; } +$filter_minbetrag = GETPOST('filter_minbetrag', 'alpha'); // Komma/Punkt zugelassen +$filter_minbetrag_num = ($filter_minbetrag !== '' && $filter_minbetrag !== null) + ? (float) str_replace(',', '.', $filter_minbetrag) + : null; +if ($filter_minbetrag_num !== null) { + $filter['min_betrag'] = $filter_minbetrag_num; +} +$filter_kundentyp = GETPOST('filter_kundentyp', 'alpha'); // '', 'B2B', 'B2C' +if ($filter_kundentyp === 'B2B' || $filter_kundentyp === 'B2C') { + $filter['kundentyp'] = $filter_kundentyp; +} llxHeader('', $langs->trans($mode === 'archiv' ? 'MahnungArchiv' : 'MahnungVorschlagsliste')); @@ -82,17 +96,46 @@ print ''; print ''; print ''; print ''; +print ''; +print ''; print ''; print ''; -print ''; + +// Mahnstufe +print ''; + +// Mindestverzug print ''; -print ''; + +// Kundentyp +print ''; + +// Kunden-Auswahl: Dolibarr-Standard select_company (Ajax wenn COMPANY_USE_SEARCH_TO_SELECT, +// sonst klassisches Dropdown). htmlname='search_socid' bleibt = Backward-Kompatibilitaet +// zu Direkt-Links (?search_socid=74) von der Kundenkarte. +print ''; + print ''; print '
'.$langs->trans('MahnungStufe').''.$langs->trans('MahnungTageVerzug').' (min)'.$langs->trans('MahnungBetragOffen').' (min)'.$langs->trans('MahnungKundentyp').''.$langs->trans('MahnungKunde').'
'; + +// Mindestbetrag (in EUR, Komma erlaubt) +print ''; +print $form->select_company( + $filter_socid, // selected + 'search_socid', // htmlname (Hidden-Input-Name) + '', // filter + 'SelectThirdParty', // showempty (Translation-Key) + 0, 0, array(), 0, 'minwidth250' +); print '
'; print '
'; @@ -143,6 +186,7 @@ function renderVorschlagsliste($db, $filter) } print ''.$langs->trans('MahnungRechnung').''; print ''.$langs->trans('MahnungKunde').''; + print ''.$langs->trans('MahnungKontakt').''; print ''.$langs->trans('MahnungKundentyp').''; print ''.$langs->trans('MahnungFaelligkeitAlt').''; print ''.$langs->trans('MahnungTageVerzug').''; @@ -159,6 +203,7 @@ function renderVorschlagsliste($db, $filter) } print ''.dol_escape_htmltag($r['facture_ref']).''; print ''.dol_escape_htmltag($r['soc_nom']).''; + print ''.renderKontaktIcons($r['soc_phone'] ?? '', $r['soc_email'] ?? '').''; print ''.dol_escape_htmltag($r['kundentyp']).''; print ''.dol_print_date($r['facture_date_lim_reglement'], 'day').''; print ''.((int) $r['tage_verzug']).''; @@ -168,7 +213,7 @@ function renderVorschlagsliste($db, $filter) print ''; $summeOffen += (float) $r['betrag_offen']; } - print ''.$langs->trans('Total').''; + print ''.$langs->trans('Total').''; print ''.price($summeOffen).''; print ''; @@ -207,6 +252,7 @@ function renderUebersprungeneTabelle($skipped) print ''; print ''.$langs->trans('MahnungRechnung').''; print ''.$langs->trans('MahnungKunde').''; + print ''.$langs->trans('MahnungKontakt').''; print ''.$langs->trans('MahnungFaelligkeitAlt').''; print ''.$langs->trans('MahnungTageVerzug').''; print ''.$langs->trans('MahnungBetragOffen').''; @@ -217,6 +263,7 @@ function renderUebersprungeneTabelle($skipped) print ''; print ''.dol_escape_htmltag($r['facture_ref']).''; print ''.dol_escape_htmltag($r['soc_nom']).''; + print ''.renderKontaktIcons($r['soc_phone'] ?? '', $r['soc_email'] ?? '').''; print ''.dol_print_date($r['facture_date_lim_reglement'], 'day').''; print ''.((int) $r['tage_verzug']).''; print ''.price($r['betrag_offen']).''; @@ -227,6 +274,33 @@ function renderUebersprungeneTabelle($skipped) print ''; } +/** + * Rendert Kontakt-Icons (Telefon + Mail) mit Direktlinks. + * + * @param string $phone + * @param string $email + * @return string HTML + */ +function renderKontaktIcons($phone, $email) +{ + $out = ''; + $phone = trim((string) $phone); + $email = trim((string) $email); + if ($phone !== '') { + $telHref = preg_replace('/[^0-9+]/', '', $phone); + $out .= '' + .img_picto('', 'fa-phone').''; + } + if ($email !== '') { + $out .= '' + .img_picto('', 'fa-envelope').''; + } + if ($out === '') { + $out = ''; + } + return $out; +} + /** * Rendert das Archiv aller bestehenden Mahnvorgaenge. *