Vorschlagsliste: Kunden-Select2 + Mindestbetrag + Kundentyp-Filter + Kontakt-Spalte [deploy]
All checks were successful
Deploy mahnung / deploy (push) Successful in 14s
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>
This commit is contained in:
parent
e4f2dd6fb2
commit
8e5e26c162
5 changed files with 103 additions and 6 deletions
|
|
@ -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`.
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ MahnungVersandNone = Kein Versand
|
|||
MahnungRef = Mahnung-Nr.
|
||||
MahnungRechnung = Rechnung
|
||||
MahnungKunde = Kunde
|
||||
MahnungKontakt = Kontakt
|
||||
MahnungKundentyp = Typ
|
||||
MahnungKundentypB2C = Privat (B2C)
|
||||
MahnungKundentypB2B = Geschaeftlich (B2B)
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ MahnungVersandNone = No dispatch
|
|||
MahnungRef = Dunning ref.
|
||||
MahnungRechnung = Invoice
|
||||
MahnungKunde = Customer
|
||||
MahnungKontakt = Contact
|
||||
MahnungKundentyp = Type
|
||||
MahnungKundentypB2C = Private (B2C)
|
||||
MahnungKundentypB2B = Business (B2B)
|
||||
|
|
|
|||
80
list.php
80
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 '<input type="hidden" name="mode" value="'.dol_escape_htmltag($mode).'">';
|
|||
print '<table class="noborder centpercent"><tr class="liste_titre">';
|
||||
print '<th>'.$langs->trans('MahnungStufe').'</th>';
|
||||
print '<th>'.$langs->trans('MahnungTageVerzug').' (min)</th>';
|
||||
print '<th>'.$langs->trans('MahnungBetragOffen').' (min)</th>';
|
||||
print '<th>'.$langs->trans('MahnungKundentyp').'</th>';
|
||||
print '<th>'.$langs->trans('MahnungKunde').'</th>';
|
||||
print '<th></th></tr>';
|
||||
print '<tr><td><select name="filter_stufe">';
|
||||
print '<tr>';
|
||||
|
||||
// Mahnstufe
|
||||
print '<td><select name="filter_stufe">';
|
||||
print '<option value="">— '.$langs->trans('All').' —</option>';
|
||||
foreach (array(1, 2, 3) as $st) {
|
||||
print '<option value="'.$st.'"'.((string) $filter_stufe === (string) $st ? ' selected' : '').'>'.$st.'</option>';
|
||||
}
|
||||
print '</select></td>';
|
||||
|
||||
// Mindestverzug
|
||||
print '<td><input type="number" name="filter_minverzug" value="'.dol_escape_htmltag((string) $filter_minverzug).'" size="4"></td>';
|
||||
print '<td><input type="number" name="search_socid" value="'.dol_escape_htmltag((string) $filter_socid).'" size="6" placeholder="rowid">';
|
||||
|
||||
// Mindestbetrag (in EUR, Komma erlaubt)
|
||||
print '<td><input type="text" name="filter_minbetrag" value="'.dol_escape_htmltag((string) $filter_minbetrag).'" size="6" placeholder="0,00"> €</td>';
|
||||
|
||||
// Kundentyp
|
||||
print '<td><select name="filter_kundentyp">';
|
||||
print '<option value="">— '.$langs->trans('All').' —</option>';
|
||||
print '<option value="B2B"'.($filter_kundentyp === 'B2B' ? ' selected' : '').'>B2B</option>';
|
||||
print '<option value="B2C"'.($filter_kundentyp === 'B2C' ? ' selected' : '').'>B2C</option>';
|
||||
print '</select></td>';
|
||||
|
||||
// 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 '<td>';
|
||||
print $form->select_company(
|
||||
$filter_socid, // selected
|
||||
'search_socid', // htmlname (Hidden-Input-Name)
|
||||
'', // filter
|
||||
'SelectThirdParty', // showempty (Translation-Key)
|
||||
0, 0, array(), 0, 'minwidth250'
|
||||
);
|
||||
print '</td>';
|
||||
|
||||
print '<td><input type="submit" class="button" value="'.$langs->trans('Search').'"></td>';
|
||||
print '</tr></table>';
|
||||
print '</form><br>';
|
||||
|
|
@ -143,6 +186,7 @@ function renderVorschlagsliste($db, $filter)
|
|||
}
|
||||
print '<th>'.$langs->trans('MahnungRechnung').'</th>';
|
||||
print '<th>'.$langs->trans('MahnungKunde').'</th>';
|
||||
print '<th>'.$langs->trans('MahnungKontakt').'</th>';
|
||||
print '<th>'.$langs->trans('MahnungKundentyp').'</th>';
|
||||
print '<th>'.$langs->trans('MahnungFaelligkeitAlt').'</th>';
|
||||
print '<th class="right">'.$langs->trans('MahnungTageVerzug').'</th>';
|
||||
|
|
@ -159,6 +203,7 @@ function renderVorschlagsliste($db, $filter)
|
|||
}
|
||||
print '<td><a href="'.DOL_URL_ROOT.'/compta/facture/card.php?id='.((int) $r['facture_id']).'">'.dol_escape_htmltag($r['facture_ref']).'</a></td>';
|
||||
print '<td><a href="'.DOL_URL_ROOT.'/societe/card.php?socid='.((int) $r['soc_id']).'">'.dol_escape_htmltag($r['soc_nom']).'</a></td>';
|
||||
print '<td class="nowrap">'.renderKontaktIcons($r['soc_phone'] ?? '', $r['soc_email'] ?? '').'</td>';
|
||||
print '<td>'.dol_escape_htmltag($r['kundentyp']).'</td>';
|
||||
print '<td>'.dol_print_date($r['facture_date_lim_reglement'], 'day').'</td>';
|
||||
print '<td class="right">'.((int) $r['tage_verzug']).'</td>';
|
||||
|
|
@ -168,7 +213,7 @@ function renderVorschlagsliste($db, $filter)
|
|||
print '</tr>';
|
||||
$summeOffen += (float) $r['betrag_offen'];
|
||||
}
|
||||
print '<tr class="liste_total"><td colspan="'.($canWrite ? 6 : 5).'" class="right">'.$langs->trans('Total').'</td>';
|
||||
print '<tr class="liste_total"><td colspan="'.($canWrite ? 7 : 6).'" class="right">'.$langs->trans('Total').'</td>';
|
||||
print '<td class="right">'.price($summeOffen).'</td><td colspan="2"></td></tr>';
|
||||
print '</table>';
|
||||
|
||||
|
|
@ -207,6 +252,7 @@ function renderUebersprungeneTabelle($skipped)
|
|||
print '<tr class="liste_titre">';
|
||||
print '<th>'.$langs->trans('MahnungRechnung').'</th>';
|
||||
print '<th>'.$langs->trans('MahnungKunde').'</th>';
|
||||
print '<th>'.$langs->trans('MahnungKontakt').'</th>';
|
||||
print '<th>'.$langs->trans('MahnungFaelligkeitAlt').'</th>';
|
||||
print '<th class="right">'.$langs->trans('MahnungTageVerzug').'</th>';
|
||||
print '<th class="right">'.$langs->trans('MahnungBetragOffen').'</th>';
|
||||
|
|
@ -217,6 +263,7 @@ function renderUebersprungeneTabelle($skipped)
|
|||
print '<tr class="oddeven">';
|
||||
print '<td><a href="'.DOL_URL_ROOT.'/compta/facture/card.php?id='.((int) $r['facture_id']).'">'.dol_escape_htmltag($r['facture_ref']).'</a></td>';
|
||||
print '<td><a href="'.DOL_URL_ROOT.'/societe/card.php?socid='.((int) $r['soc_id']).'">'.dol_escape_htmltag($r['soc_nom']).'</a></td>';
|
||||
print '<td class="nowrap">'.renderKontaktIcons($r['soc_phone'] ?? '', $r['soc_email'] ?? '').'</td>';
|
||||
print '<td>'.dol_print_date($r['facture_date_lim_reglement'], 'day').'</td>';
|
||||
print '<td class="right">'.((int) $r['tage_verzug']).'</td>';
|
||||
print '<td class="right">'.price($r['betrag_offen']).'</td>';
|
||||
|
|
@ -227,6 +274,33 @@ function renderUebersprungeneTabelle($skipped)
|
|||
print '</table>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 .= '<a href="tel:'.dol_escape_htmltag($telHref).'" title="'.dol_escape_htmltag($phone).'" class="marginrightonly">'
|
||||
.img_picto('', 'fa-phone').'</a>';
|
||||
}
|
||||
if ($email !== '') {
|
||||
$out .= '<a href="mailto:'.dol_escape_htmltag($email).'" title="'.dol_escape_htmltag($email).'">'
|
||||
.img_picto('', 'fa-envelope').'</a>';
|
||||
}
|
||||
if ($out === '') {
|
||||
$out = '<span class="opacitymedium">—</span>';
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rendert das Archiv aller bestehenden Mahnvorgaenge.
|
||||
*
|
||||
|
|
|
|||
Loading…
Reference in a new issue