* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. */ /** * \file mahnung/list.php * \ingroup mahnung * \brief Vorschlagsliste (mode=vorschlag) und Mahnvorgänge-Archiv (mode=archiv). */ $res = 0; if (!$res && !empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) { $res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php"; } $tmp = empty($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME']; $tmp2 = realpath(__FILE__); $i = strlen($tmp) - 1; $j = strlen($tmp2) - 1; while ($i > 0 && $j > 0 && isset($tmp[$i]) && isset($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) { $i--; $j--; } if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1))."/main.inc.php")) { $res = @include substr($tmp, 0, ($i + 1))."/main.inc.php"; } if (!$res && file_exists("../main.inc.php")) { $res = @include "../main.inc.php"; } if (!$res && file_exists("../../main.inc.php")) { $res = @include "../../main.inc.php"; } if (!$res) { die("Include of main fails"); } 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')); if (!$user->hasRight('mahnung', 'read')) { accessforbidden(); } $form = new Form($db); $mode = GETPOST('mode', 'aZ09'); if ($mode !== 'archiv') { $mode = 'vorschlag'; } $filter = array(); $filter_stufe = GETPOST('filter_stufe', 'int'); if ($filter_stufe !== '' && $filter_stufe !== null) { $filter['stufe'] = (int) $filter_stufe; } $filter_minverzug = GETPOST('filter_minverzug', 'int'); if ($filter_minverzug !== '' && $filter_minverzug !== null) { $filter['min_tage_verzug'] = (int) $filter_minverzug; } $filter_socid = GETPOST('search_socid', 'int'); // select_company liefert -1 für "nichts ausgewählt" — nur positive IDs als Filter werten if ((int) $filter_socid > 0) { $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')); print load_fiche_titre( $langs->trans($mode === 'archiv' ? 'MahnungArchiv' : 'MahnungVorschlagsliste'), '', 'fa-envelope-open-text' ); // --- Filter-Form --- print '
'; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; // Mahnstufe print ''; // Mindestverzug print ''; // Mindestbetrag (in EUR, Komma erlaubt) print ''; // Kundentyp — Auto-Submit beim Wechsel + Kunden-Auswahl zurücksetzen, damit das // Kunden-Dropdown direkt mit dem neuen Typ-Filter neu geladen wird (alte Auswahl // könnte im neuen Filter gar nicht mehr enthalten sein). print ''; // Kunden-Auswahl: Dolibarr-Standard select_company (Ajax wenn COMPANY_USE_SEARCH_TO_SELECT, // sonst klassisches Dropdown). htmlname='search_socid' bleibt = Backward-Kompatibilität // zu Direkt-Links (?search_socid=74) von der Kundenkarte. Wenn Kundentyp-Filter // gesetzt ist, schränken wir die Dropdown-Liste passend ein. // // WICHTIG: select_thirdparty_list erwartet $filter im Universal-Search-Criteria-Format // (siehe forgeSQLFromUniversalSearchCriteria), NICHT plain SQL. // Syntax: (feld:operator:wert) mit AND/OR; Operatoren: =, !=, <, >, like, is, isnot, in, notin. $socFilter = ''; if ($filter_kundentyp === 'B2B') { $socFilter = "(s.tva_intra:isnot:NULL) AND (s.tva_intra:!=:'')"; } elseif ($filter_kundentyp === 'B2C') { $socFilter = "(s.tva_intra:is:NULL) OR (s.tva_intra:=:'')"; } print ''; print ''; print '
'.$langs->trans('MahnungStufe').''.$langs->trans('MahnungTageVerzug').' (min)'.$langs->trans('MahnungBetragOffen').' (min)'.$langs->trans('MahnungKundentyp').''.$langs->trans('MahnungKunde').'
'; print $form->select_company( $filter_socid, // selected 'search_socid', // htmlname (Hidden-Input-Name) $socFilter, // filter (SQL-Condition, von Dolibarr in WHERE eingebunden) 'SelectThirdParty', // showempty (Translation-Key) 0, 0, array(), 0, 'minwidth250' ); print '
'; print '

'; if ($mode === 'vorschlag') { renderVorschlagsliste($db, $filter); } else { renderArchiv($db, $filter); } llxFooter(); $db->close(); /** * Rendert die Vorschlagsliste auf Basis von MahnungVorschlag. * * @param DoliDB $db * @param array $filter * @return void */ function renderVorschlagsliste($db, $filter) { global $langs, $user; $service = new MahnungVorschlag($db); $rows = $service->getVorschlaege($filter); $skipped = $service->getUebersprungeneRechnungen($filter); if (empty($rows) && empty($skipped)) { print '
'.$langs->trans('MahnungKeineUeberfaelligen').'
'; return; } if (empty($rows)) { print '
'.$langs->trans('MahnungKeineUeberfaelligen').'
'; renderUebersprungeneTabelle($skipped); return; } $canWrite = $user->hasRight('mahnung', 'write'); print '
'; print ''; print ''; print ''; if ($canWrite) { print ''; } print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; $summeOffen = 0.0; foreach ($rows as $r) { print ''; if ($canWrite) { print ''; } print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; $summeOffen += (float) $r['betrag_offen']; } print ''; print ''; print '
'.$langs->trans('MahnungRechnung').''.$langs->trans('MahnungKunde').''.$langs->trans('MahnungKontakt').''.$langs->trans('MahnungKundentyp').''.$langs->trans('MahnungFaelligkeitAlt').''.$langs->trans('MahnungTageVerzug').''.$langs->trans('MahnungBetragOffen').''.$langs->trans('MahnungLetzteMahnung').''.$langs->trans('MahnungVorgeschlageneStufe').'
'.dol_escape_htmltag($r['facture_ref']).''.dol_escape_htmltag($r['soc_nom']).''.renderKontaktIcons($r['soc_phone'] ?? '', $r['soc_email'] ?? '').''.dol_escape_htmltag($r['kundentyp']).''.dol_print_date($r['facture_date_lim_reglement'], 'day').''.((int) $r['tage_verzug']).''.price($r['betrag_offen']).''.($r['letzte_mahnung_stufe'] ? $langs->trans('MahnungStufeAmDatum', ((int) $r['letzte_mahnung_stufe']), dol_print_date($r['letzte_mahnung_datum'], 'day')) : '—').''.((int) $r['vorgeschlagene_stufe']).' — '.dol_escape_htmltag($r['vorgeschlagene_stufe_label']).'
'.$langs->trans('Total').''.price($summeOffen).'
'; if ($canWrite) { print '
'; print ' '; print ''; print '
'; print ''; } print '
'; renderUebersprungeneTabelle($skipped); } /** * Diagnose-Tabelle: überfällige Rechnungen, die aktuell nicht vorgeschlagen werden, * inklusive Grund (Wartefrist, Stufen ausgeschöpft, ...). * * @param array $skipped * @return void */ function renderUebersprungeneTabelle($skipped) { global $langs; if (empty($skipped)) { return; } print '

'.$langs->trans('MahnungUebersprungen').' ('.count($skipped).')

'; print '
'.$langs->trans('MahnungUebersprungenHint').'
'; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; foreach ($skipped as $r) { print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; } print '
'.$langs->trans('MahnungRechnung').''.$langs->trans('MahnungKunde').''.$langs->trans('MahnungKontakt').''.$langs->trans('MahnungFaelligkeitAlt').''.$langs->trans('MahnungTageVerzug').''.$langs->trans('MahnungBetragOffen').''.$langs->trans('MahnungLetzteMahnung').''.$langs->trans('MahnungSkipGrund').'
'.dol_escape_htmltag($r['facture_ref']).''.dol_escape_htmltag($r['soc_nom']).''.renderKontaktIcons($r['soc_phone'] ?? '', $r['soc_email'] ?? '').''.dol_print_date($r['facture_date_lim_reglement'], 'day').''.((int) $r['tage_verzug']).''.price($r['betrag_offen']).''.($r['letzte_mahnung_stufe'] ? $langs->trans('MahnungStufeAmDatum', ((int) $r['letzte_mahnung_stufe']), dol_print_date($r['letzte_mahnung_datum'], 'day')) : '—').''.dol_escape_htmltag((string) $r['skip_reason']).'
'; } /** * 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 Mahnvorgänge. * * @param DoliDB $db * @param array $filter * @return void */ function renderArchiv($db, $filter) { global $langs; $mahnungObj = new Mahnung($db); $archivFilter = array(); if (isset($filter['stufe'])) { $archivFilter['stufe'] = $filter['stufe']; } $mahnungen = $mahnungObj->fetchAll('date_mahnung', 'DESC', 200, 0, $archivFilter); if (empty($mahnungen)) { print '
'.$langs->trans('MahnungKeineVorgaenge').'
'; return; } // Refs für alle in der Liste vorkommenden Rechnungen+Kunden in zwei Queries holen $socIds = array(); $factIds = array(); foreach ($mahnungen as $m) { $socIds[(int) $m->fk_soc] = true; $factIds[(int) $m->fk_facture] = true; } $socMap = array(); if (!empty($socIds)) { $resql = $db->query("SELECT rowid, nom FROM ".MAIN_DB_PREFIX."societe WHERE rowid IN (".implode(',', array_keys($socIds)).")"); while ($resql && ($r = $db->fetch_object($resql))) { $socMap[(int) $r->rowid] = $r->nom; } } $factMap = array(); if (!empty($factIds)) { $resql = $db->query("SELECT rowid, ref FROM ".MAIN_DB_PREFIX."facture WHERE rowid IN (".implode(',', array_keys($factIds)).")"); while ($resql && ($r = $db->fetch_object($resql))) { $factMap[(int) $r->rowid] = $r->ref; } } print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; foreach ($mahnungen as $m) { $factureRef = $factMap[(int) $m->fk_facture] ?? ('#'.(int) $m->fk_facture); $socName = $socMap[(int) $m->fk_soc] ?? ('#'.(int) $m->fk_soc); print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; } print '
'.$langs->trans('MahnungRef').''.$langs->trans('MahnungRechnung').''.$langs->trans('MahnungKunde').''.$langs->trans('MahnungStufe').''.$langs->trans('MahnungDatum').''.$langs->trans('MahnungBetragOffen').''.$langs->trans('MahnungGebuehr').''.$langs->trans('MahnungVerzugszinsen').''.$langs->trans('MahnungSumme').''.$langs->trans('Status').'
'.dol_escape_htmltag($m->ref).''.dol_escape_htmltag($factureRef).''.dol_escape_htmltag($socName).''.((int) $m->stufe).''.dol_print_date($m->date_mahnung, 'day').''.price($m->betrag_offen).''.price((float) $m->mahngebuehr + (float) $m->pauschale_b2b).''.price($m->verzugszinsen).''.price($m->summe_mahnung).''.dol_escape_htmltag($m->getStatusLabel()).'
'; }