* * 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'; // Nur wegen mahnungVorschlagFehlertext() — damit der Fehlertext des Vorschlag-Service // überall identisch aufbereitet wird (eine Quelle statt kopierter Fallback-Logik). require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/lib/mahnung_anlage.lib.php'; require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/lib/mahnung_ui.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php'; require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/core/modules/modMahnung.class.php'; global $langs, $user, $db; // Schema nach einem reinen Datei-Deploy nachziehen (Pipeline ruft weder init() // noch zwangsläufig die Setup-Seite auf). Im Regelfall nur ein Konstanten- // Vergleich ohne Query — siehe modMahnung::ensureSchema(). modMahnung::ensureSchema($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(); // Stufen-Filter: 0 ist eine gültige Stufennummer (kostenlose Zahlungserinnerung), // "nicht gesetzt" darf deshalb NIE über empty()/falsy entschieden werden. // Auch GETPOST(..., 'int') taugt hier nicht als alleinige Weiche: je nach // Dolibarr-Version liefert es für einen leeren Parameter '' oder 0 — im zweiten // Fall würde die Option "— Alle —" stumm zu "nur Stufe 0" werden und Eddy sähe // keine einzige echte Mahnung mehr. Deshalb den Rohwert prüfen: nur eine echte // Ganzzahl im Request setzt den Filter. $filter_stufe ist danach '' oder int. $filter_stufe = ''; $filterStufeRaw = trim((string) GETPOST('filter_stufe', 'alphanohtml')); if ($filterStufeRaw !== '' && preg_match('/^-?\d+$/', $filterStufeRaw)) { $filter_stufe = (int) $filterStufeRaw; $filter['stufe'] = $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'), mahnungSetupLink(), 'fa-envelope-open-text' ); // --- Filter-Form --- print '
'; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; // Mahnstufe — Optionen kommen aus der Konfiguration, NICHT hartkodiert 1/2/3. // Stufennummern sind frei wählbar und Stufe 0 (Zahlungserinnerung) muss filterbar sein. $stufenOptionen = array(); $stufenFilterObj = new MahnungStufe($db); foreach ($stufenFilterObj->fetchAllActive() as $s) { $stufenOptionen[(int) $s->stufe] = (string) $s->label; } // Eine bereits gewählte Stufe muss wählbar bleiben, auch wenn sie inzwischen // deaktiviert wurde — sonst kippt der Filter beim nächsten Absenden stumm auf "Alle". // Im Archiv-Modus gibt es zudem Mahnvorgänge zu nicht mehr aktiven Stufen. if ((string) $filter_stufe !== '' && !isset($stufenOptionen[(int) $filter_stufe])) { $stufenOptionen[(int) $filter_stufe] = ''; } ksort($stufenOptionen, SORT_NUMERIC); 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 — deckungsgleich mit // MahnungVorschlag::ermittleKundentyp(), siehe mahnungBaueKundentypSocFilter(). $socFilter = mahnungBaueKundentypSocFilter($db, $filter_kundentyp); 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 im USC-Format, von Dolibarr in die 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); // Fehler und Leerstand strikt trennen: getVorschlaege() liefert false, wenn die // Vorschlagslogik gar nicht laufen konnte (SQL-Fehler oder keine aktive Mahnstufe // konfiguriert) — array() dagegen bedeutet "es gibt wirklich nichts zu mahnen". // Ohne diese Prüfung würde empty(false) === true den Fehler als "keine // überfälligen Rechnungen" tarnen, also als "alles bezahlt". // // Reihenfolge ist wichtig: getUebersprungeneRechnungen() setzt $service->error // zurück, deshalb wird das Ergebnis von getVorschlaege() ZUERST ausgewertet. $rows = $service->getVorschlaege($filter); if ($rows === false) { print '
'.dol_escape_htmltag(mahnungVorschlagFehlertext($service)).'
'; return; } // Die Übersprungen-Tabelle ist reine Diagnose. Scheitert nur sie, bleibt die // Vorschlagsliste nutzbar — Hinweis ausgeben und ohne die Diagnose weitermachen. $skipped = $service->getUebersprungeneRechnungen($filter); if ($skipped === false) { print '
'.dol_escape_htmltag( $langs->transnoentities('MahnungUebersprungenFehler', mahnungVorschlagFehlertext($service)) ).'
'; $skipped = array(); } 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']).''.renderLetzteMahnungZelle($r).''.renderVorschlagStufeZelle($r).'
'.$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']).''.renderLetzteMahnungZelle($r).''.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; } /** * Zelle "Letzte Mahnung" für Vorschlags- und Übersprungen-Tabelle. * * WICHTIG — KEIN Truthy-Check auf die Stufennummer: Stufennummern sind frei * konfigurierbar und 0 (kostenlose Zahlungserinnerung) ist eine gültige Stufe. * Mit `$r['letzte_mahnung_stufe'] ? ... : '—'` stand nach einer versendeten * Erinnerung ein Strich in der Zelle, also "noch nie gemahnt" — genau der * Zustand, an dem Eddy die Wartefrist bis zur Folgestufe abliest. * MahnungVorschlag liefert null, wenn es wirklich keine Vormahnung gibt. * * @param array $r Vorschlags-/Skip-Zeile aus MahnungVorschlag * @return string HTML */ function renderLetzteMahnungZelle(array $r) { global $langs; if (!isset($r['letzte_mahnung_stufe'])) { return ''; } $datum = !empty($r['letzte_mahnung_datum']) ? dol_print_date($r['letzte_mahnung_datum'], 'day') : ''; // transnoentities() statt trans(): trans() liefert HTML-Entities, die zusammen // mit einem zusätzlichen dol_escape_htmltag() doppelt kodiert würden. // Beide Platzhalter sind hier unkritisch (int bzw. formatiertes Datum). return $langs->transnoentities('MahnungStufeAmDatum', ((int) $r['letzte_mahnung_stufe']), $datum); } /** * Zelle "Vorgeschlagene Stufe" inkl. Badge kostenlos/kostenpflichtig. * * Ob eine Stufe die kostenlose Zahlungserinnerung ist, entscheidet ausschließlich * das Flag ist_erinnerung der Stufe (von MahnungVorschlag als * 'vorgeschlagene_stufe_ist_erinnerung' mitgeliefert) — NICHT die Stufennummer. * Ohne das Badge war der Liste nicht anzusehen, ob ein Häkchen eine kostenlose * E-Mail-Erinnerung oder eine kostenpflichtige Mahnung mit Gebühr, §288-Pauschale * und Verzugszinsen auslöst. * * Farbskala bewusst identisch zu core/boxes/box_mahnung_offen.php. * * @param array $r Vorschlags-Zeile aus MahnungVorschlag * @return string HTML */ function renderVorschlagStufeZelle(array $r) { global $langs; // Nummer und Bezeichnung stehen zusammen im Badge — kein zweites Etikett // daneben, das nur dasselbe Wort wiederholt (siehe lib/mahnung_ui.lib.php). return mahnungStufeBadge( (int) $r['vorgeschlagene_stufe'], isset($r['vorgeschlagene_stufe_label']) ? (string) $r['vorgeschlagene_stufe_label'] : '', !empty($r['vorgeschlagene_stufe_ist_erinnerung']) ); } /** * Baut den USC-Filter für das Kunden-Dropdown passend zum gewählten Kundentyp. * * Muss deckungsgleich zu MahnungVorschlag::ermittleKundentyp() bleiben: dort ist * ein Kunde B2B, sobald tva_intra ODER siret ODER siren gefüllt ist oder fk_typent * auf einen Code der B2B-Whitelist zeigt. Der Dropdown-Filter schränkte bis hierher * nur über tva_intra ein — dadurch tauchten bei Kundentyp "B2C" Kleinunternehmer * ohne USt-IdNr. im Dropdown auf, die die Vorschlagslogik anschließend als B2B * wieder herausfilterte: der Kunde war wählbar, die Liste danach leer. * * WICHTIG: select_thirdparty_list() erwartet das Universal-Search-Criteria-Format * (siehe forgeSQLFromUniversalSearchCriteria), NICHT plain SQL. Syntax * (feld:operator:wert), verknüpft mit AND/OR, Klammerung ist erlaubt. Der * Tabellen-Alias der Kundentabelle ist dort immer 's'. * * Bewusst nur die Operatoren =, !=, is und isnot: IN/NOTIN wären kürzer, sind aber * je nach Dolibarr-Version unterschiedlich implementiert, und ein nicht erkannter * Operator endet in einem SQL-Syntaxfehler mit HTTP 500 statt in einer leeren Liste. * * @param DoliDB $db * @param string $kundentyp '' | 'B2B' | 'B2C' * @return string USC-Filterstring, '' = kein Filter */ function mahnungBaueKundentypSocFilter($db, $kundentyp) { if ($kundentyp !== 'B2B' && $kundentyp !== 'B2C') { return ''; } $typentIds = mahnungB2bTypentIds($db); if ($kundentyp === 'B2B') { // Eines der Merkmale genügt -> ODER-Verknüpfung $teile = array( "((s.tva_intra:isnot:NULL) AND (s.tva_intra:!=:''))", "((s.siret:isnot:NULL) AND (s.siret:!=:''))", "((s.siren:isnot:NULL) AND (s.siren:!=:''))", ); foreach ($typentIds as $id) { $teile[] = "(s.fk_typent:=:".((int) $id).")"; } return '('.implode(' OR ', $teile).')'; } // B2C ist die Negation: KEIN einziges B2B-Merkmal darf zutreffen. $teile = array( "((s.tva_intra:is:NULL) OR (s.tva_intra:=:''))", "((s.siret:is:NULL) OR (s.siret:=:''))", "((s.siren:is:NULL) OR (s.siren:=:''))", ); foreach ($typentIds as $id) { // fk_typent IS NULL muss explizit mit rein: "fk_typent != 5" ist bei NULL // nicht wahr, sondern NULL — der Kunde fiele sonst aus der B2C-Liste heraus. $teile[] = "((s.fk_typent:is:NULL) OR (s.fk_typent:!=:".((int) $id)."))"; } return '('.implode(' AND ', $teile).')'; } /** * Löst die B2B-Typ-Codes der Whitelist auf ihre IDs in llx_c_typent auf. * * Quelle ist dieselbe wie in MahnungVorschlag::getB2bTypentCodes(): die Konstante * MAHNUNG_B2B_TYPENT_CODES, ersatzweise MahnungVorschlag::B2B_TYPENT_CODES_DEFAULT. * Ein leerer Wert schaltet die fk_typent-Auswertung ab — dann bleibt auch der * Dropdown-Filter bei tva_intra/siret/siren. Der Wert wird von Hand gepflegt, * deshalb tolerant einlesen (trimmen, auf Großschreibung normalisieren). * * llx_c_typent wird bewusst OHNE "active = 1" abgefragt: ermittleKundentyp() joint * die Tabelle ebenfalls ohne Active-Filter. Ein deaktivierter Typ würde sonst in der * Auswertung B2B ergeben, im Dropdown aber fehlen — genau die Divergenz, die hier * beseitigt werden soll. * * @param DoliDB $db * @return int[] leeres Array = fk_typent nicht auswerten */ function mahnungB2bTypentIds($db) { static $cache = null; if ($cache !== null) { return $cache; } $cache = array(); $codesQuoted = array(); $raw = getDolGlobalString('MAHNUNG_B2B_TYPENT_CODES', MahnungVorschlag::B2B_TYPENT_CODES_DEFAULT); foreach (explode(',', (string) $raw) as $code) { $code = strtoupper(trim($code)); if ($code !== '') { $codesQuoted[] = "'".$db->escape($code)."'"; } } if (empty($codesQuoted)) { return $cache; } $sql = "SELECT id FROM ".MAIN_DB_PREFIX."c_typent"; $sql .= " WHERE code IN (".implode(',', $codesQuoted).")"; $resql = $db->query($sql); if (!$resql) { // Kein harter Abbruch: ohne die IDs bleibt der Dropdown-Filter bei // tva_intra/siret/siren, die Liste selbst funktioniert weiter. dol_syslog('mahnung/list.php: Auflösung der B2B-Typ-Codes fehlgeschlagen: '.$db->lasterror(), LOG_ERR); return $cache; } while ($obj = $db->fetch_object($resql)) { $cache[] = (int) $obj->id; } $db->free($resql); return $cache; } /** * 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); // fetchAll() liefert bei einem SQL-Fehler -1 statt array(). empty(-1) ist false, // der Wert liefe also ungeprüft in die foreach-Schleifen (PHP-Warning + leere // Tabelle). Fehler und Leerstand deshalb auch hier trennen. if (!is_array($mahnungen)) { $detail = !empty($mahnungObj->error) ? (string) $mahnungObj->error : ''; print '
'.dol_escape_htmltag($langs->transnoentities('MahnungArchivFehler', $detail)).'
'; return; } 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()).'
'; }