* * 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/admin/setup.php * \ingroup mahnung * \brief Setup: Mahnstufen (frei anlegbar, bearbeitbar und löschbar), Basiszins, * B2C/B2B-Aufschläge, Pauschale, Ntfy-Topic. */ $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 && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php")) { $res = @include dirname(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.'/core/lib/admin.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; 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/core/modules/mahnung/modules_mahnung.php'; require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/core/modules/modMahnung.class.php'; global $langs, $user, $conf, $db; $langs->loadLangs(array('admin', 'mahnung@mahnung')); if (!$user->admin && !$user->hasRight('mahnung', 'setup')) { accessforbidden(); } // Schema-Migration bei jedem Setup-Aufruf (idempotent — fehlende Spalten ergänzen) (new modMahnung($db))->migrateVersandFelder(); // Tracking-Pattern-Tabelle anlegen (falls noch nicht da) + Default-Patterns seeden require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/class/mahnungtrackingpattern.class.php'; $check = $db->query("SHOW TABLES LIKE '".$db->escape(MAIN_DB_PREFIX.'mahnung_trackingpattern')."'"); if ($check && $db->num_rows($check) == 0) { $db->query("CREATE TABLE ".MAIN_DB_PREFIX."mahnung_trackingpattern (" ." rowid INTEGER AUTO_INCREMENT PRIMARY KEY," ." entity INTEGER DEFAULT 1 NOT NULL," ." provider VARCHAR(20) NOT NULL," ." label VARCHAR(80) NOT NULL," ." regex VARCHAR(255) NOT NULL," ." url_template VARCHAR(255) NOT NULL," ." priority INTEGER DEFAULT 100," ." active TINYINT DEFAULT 1," ." datec DATETIME," ." tms TIMESTAMP," ." INDEX idx_trackingpattern_active (active, priority)," ." INDEX idx_trackingpattern_entity (entity)" .") ENGINE=InnoDB"); } if ($check) { $db->free($check); } MahnungTrackingPattern::seedDefaults($db); $form = new Form($db); $action = GETPOST('action', 'aZ09'); $confirm = GETPOST('confirm', 'alpha'); $rowid = GETPOSTINT('rowid'); // --- CSRF-Absicherung aller zustandsändernden Aktionen ---------------------- // Der Core-Check aus main.inc.php greift auf dieser Installation NICHT: in der // conf.php steht $dolibarr_nocsrfcheck=1, zusätzlich ist die Konstante // MAIN_SECURITY_CSRF_WITH_TOKEN=0. Der komplette Prüfblock ab main.inc.php:333 // wird damit übersprungen — die Formulare schicken zwar newToken() mit, es hat // bisher nur nie jemand verglichen. Ohne eigene Prüfung reicht deshalb ein // // auf einer beliebigen fremden Seite, um im eingeloggten Kontext still eine // Mahnstufe zu löschen. Die Löschsperre weiter unten hilft dagegen nicht: sie // greift nur, wenn bereits Mahnvorgänge auf die Stufennummer verweisen — die // frisch geseedete Zahlungserinnerung (Stufe 0) ist genau der ungeschützte Fall. // // Verglichen wird gegen BEIDE Session-Slots: main.inc.php rollt das Token bei // jedem Seitenaufruf ($_SESSION['token'] = das bisherige $_SESSION['newtoken']). // Ohne MAIN_SECURITY_CSRF_TOKEN_RENEWAL_ON_EACH_CALL sind beide identisch; mit // der Option steckt das mitgeschickte Token in 'token', während 'newtoken' schon // neu gewürfelt ist — eine Prüfung nur gegen 'newtoken' würde dann jede Aktion // dieser Seite blockieren. hash_equals() statt ===: konstante Laufzeit. $tokenGesendet = (string) GETPOST('token', 'alphanohtml'); $tokenOk = ($tokenGesendet !== '' && $tokenGesendet !== 'notrequired'); if ($tokenOk) { $tokenOk = ((!empty($_SESSION['token']) && hash_equals((string) $_SESSION['token'], $tokenGesendet)) || (!empty($_SESSION['newtoken']) && hash_equals((string) $_SESSION['newtoken'], $tokenGesendet))); } // Alle Actions mit Seiteneffekt. 'ask_delete_stufe' steht bewusst NICHT drin: // die rendert nur den Bestätigungsdialog und ändert nichts in der DB. // Neue schreibende Actions gehören hier zwingend mit rein. // 'deletefile' wird weiter unten von actions_setmoduleoptions.inc.php abgearbeitet // (löscht ein hochgeladenes ODT-Template) und ist genauso ungeschützt — der Link // im ODT-Modell hängt sein Token an, geprüft hat es bisher niemand. $actionsMitToken = array( 'save_consts', 'save_stufen', 'add_stufe', 'confirm_delete_stufe', 'setdoc', 'set', 'del', 'deletefile', ); // Zusätzlich echtes POST erzwingen — aber nur dort, wo das Formular auch per // POST abschickt. NICHT bei confirm_delete_stufe: $form->formconfirm() liefert // das "Ja" bei aktivem JavaScript per GET aus ($postconfirmas = 'GET' ist in // html.form.class.php fest verdrahtet, der Dialog navigiert per location.href). // Eine POST-Pflicht würde das Löschen komplett lahmlegen. Ebenso ausgenommen // setdoc/set/del/deletefile — das sind Links in der Dokumentenmodell-Tabelle. // Auf diesen Wegen wird das Token trotzdem mitgeschickt (in der URL) und für // sich genommen reicht es: erraten kann ein Angreifer es nicht. $actionsNurPost = array('save_consts', 'save_stufen', 'add_stufe'); if (in_array($action, $actionsMitToken, true)) { $csrfFehler = ''; if (!$tokenOk) { $csrfFehler = $langs->trans('MahnungCsrfTokenUngueltig'); } elseif (in_array($action, $actionsNurPost, true) && (empty($_SERVER['REQUEST_METHOD']) || $_SERVER['REQUEST_METHOD'] !== 'POST')) { $csrfFehler = $langs->trans('MahnungNurPostErlaubt'); } if ($csrfFehler !== '') { setEventMessages($csrfFehler, null, 'errors'); header('Location: '.$_SERVER['PHP_SELF']); exit; } } // Nach fehlgeschlagener Validierung: die eingegebenen Werte bleiben im Formular stehen $stufenPost = array(); // rowid => MahnungStufe mit den POST-Werten $neuStufePost = null; // Vorbelegung des Anlege-Formulars /** * Auswahlliste der Versandarten: DB-Wert => Lang-Key. * * @return array */ function mahnungSetupVersandarten() { return array( 'pdf' => 'MahnungVersandPdf', 'mail' => 'MahnungVersandMail', 'druck' => 'MahnungVersandDruck', 'none' => 'MahnungVersandNone', ); } /** * Alle Mahnstufen der Entity laden — inklusive der deaktivierten. * fetchAllActive() filtert auf active = 1; die Setup-Seite muss aber auch * abgeschaltete Stufen anzeigen und bearbeitbar halten. * * @param DoliDB $db Datenbank-Handler * @param int $entity Entity * @return MahnungStufe[] Aufsteigend nach Stufennummer */ function mahnungSetupAlleStufen($db, $entity) { $result = array(); $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."mahnung_stufe"; $sql .= " WHERE entity = ".((int) $entity); $sql .= " ORDER BY stufe ASC"; $resql = $db->query($sql); if (!$resql) { return $result; } $ids = array(); while ($obj = $db->fetch_object($resql)) { $ids[] = (int) $obj->rowid; } $db->free($resql); foreach ($ids as $id) { $s = new MahnungStufe($db); if ($s->fetch($id) > 0) { $result[] = $s; } } return $result; } /** * Anzahl Mahnvorgänge je Stufennummer — Grundlage für die Löschsperre in der Liste. * * @param DoliDB $db Datenbank-Handler * @return array Stufennummer => Anzahl Mahnvorgänge */ function mahnungSetupStufenBelegung($db) { $counts = array(); $sql = "SELECT stufe, COUNT(*) as nb FROM ".MAIN_DB_PREFIX."mahnung_mahnung"; $sql .= " WHERE entity IN (".getEntity('mahnung').")"; $sql .= " GROUP BY stufe"; $resql = $db->query($sql); if (!$resql) { return $counts; } while ($obj = $db->fetch_object($resql)) { $counts[(int) $obj->stufe] = (int) $obj->nb; } $db->free($resql); return $counts; } /** * Zählt die Mahnvorgänge, die auf eine Stufennummer verweisen. * Bei einem SQL-Fehler wird -1 geliefert — der Aufrufer sperrt dann das Löschen, * damit im Zweifel keine Konfiguration verschwindet. * * @param DoliDB $db Datenbank-Handler * @param int $stufe Stufennummer * @return int Anzahl, -1 bei Fehler */ function mahnungSetupZaehleMahnungen($db, $stufe) { $sql = "SELECT COUNT(*) as nb FROM ".MAIN_DB_PREFIX."mahnung_mahnung"; $sql .= " WHERE stufe = ".((int) $stufe); $sql .= " AND entity IN (".getEntity('mahnung').")"; $resql = $db->query($sql); if (!$resql) { return -1; } $obj = $db->fetch_object($resql); $db->free($resql); return $obj ? (int) $obj->nb : -1; } /** * Betragsfeld aus dem POST lesen (Komma als Dezimaltrenner erlaubt) und prüfen. * Wurde das Feld gar nicht abgeschickt (weil das Formular es nicht gerendert hat), * bleibt der bisherige Wert erhalten. * * @param string $name Feldname im Formular * @param float $current Bisheriger Wert * @param string $bezeich Stufenbezeichnung für die Fehlermeldung * @param string $label Feldbezeichnung für die Fehlermeldung * @param string[] $errors Fehlerliste (by reference) * @return float */ function mahnungSetupBetrag($name, $current, $bezeich, $label, &$errors) { global $langs; if (!GETPOSTISSET($name)) { return (float) $current; } $raw = trim((string) GETPOST($name, 'alphanohtml')); if ($raw === '') { return 0; } $val = str_replace(',', '.', $raw); if (!is_numeric($val) || (float) $val < 0) { $errors[] = $langs->transnoentities('MahnungStufeWertUngueltig', dol_escape_htmltag($bezeich), dol_escape_htmltag($label)); return (float) $current; } return (float) $val; } /** * Ganzzahliges Tagesfeld aus dem POST lesen und auf >= 0 prüfen. * * @param string $name Feldname im Formular * @param int $current Bisheriger Wert * @param string $bezeich Stufenbezeichnung für die Fehlermeldung * @param string $label Feldbezeichnung für die Fehlermeldung * @param string[] $errors Fehlerliste (by reference) * @return int */ function mahnungSetupTage($name, $current, $bezeich, $label, &$errors) { global $langs; if (!GETPOSTISSET($name)) { return (int) $current; } $raw = trim((string) GETPOST($name, 'alphanohtml')); if ($raw === '') { return 0; } if (!preg_match('/^\d+$/', $raw)) { $errors[] = $langs->transnoentities('MahnungStufeWertUngueltig', dol_escape_htmltag($bezeich), dol_escape_htmltag($label)); return (int) $current; } return (int) $raw; } /** * Zinssatz-Override aus dem POST lesen und auf den Bereich 0..100 prüfen. * Leeres Feld = NULL = Standardzinssatz (Basiszins + Aufschlag) verwenden. * * @param string $name Feldname im Formular * @param float|null $current Bisheriger Wert * @param string $bezeich Stufenbezeichnung für die Fehlermeldung * @param string $label Feldbezeichnung für die Fehlermeldung * @param string[] $errors Fehlerliste (by reference) * @return float|null */ function mahnungSetupZinssatz($name, $current, $bezeich, $label, &$errors) { global $langs; $fallback = ($current === null || $current === '') ? null : (float) $current; if (!GETPOSTISSET($name)) { return $fallback; } $raw = trim((string) GETPOST($name, 'alphanohtml')); if ($raw === '') { return null; } $val = str_replace(',', '.', $raw); if (!is_numeric($val) || (float) $val < 0 || (float) $val > 100) { $errors[] = $langs->transnoentities('MahnungStufeZinssatzUngueltig', dol_escape_htmltag($bezeich), dol_escape_htmltag($label)); return $fallback; } return (float) $val; } /** * Formularwerte einer Stufe aus dem POST übernehmen und prüfen. * * Kein Feld wird abhängig vom Erinnerungs-Flag verworfen oder überschrieben: alles, * was im Formular steht, landet auch so in der DB. Ob eine Stufe die kostenlose * Zahlungserinnerung ist, wirkt sich erst zur Anlagezeit aus (mahnungBaueVorgang()). * * Felder, die gar nicht gerendert wurden (z. B. weil ein anderer Block das Formular * abgeschickt hat), kommen nicht im POST an — dann bleibt der bisherige Wert stehen. * Deshalb überall GETPOSTISSET(). * * @param MahnungStufe $s Zielobjekt (geladen oder frisch angelegt) * @param string $prefix Feld-Prefix im Formular * @param string $bezeich Stufenbezeichnung für Fehlermeldungen * @param string[] $errors Fehlerliste (by reference) * @return void */ function mahnungSetupApplyPost($s, $prefix, $bezeich, &$errors) { global $langs; // Das Erinnerungs-Flag steuert nur noch die Anzeige und die Anlage-Logik — // die übrigen Formularwerte werden davon unabhängig gelesen und gespeichert. $s->ist_erinnerung = GETPOSTISSET($prefix.'ist_erinnerung') ? 1 : 0; $s->active = GETPOSTISSET($prefix.'active') ? 1 : 0; if (GETPOSTISSET($prefix.'label')) { $s->label = trim((string) GETPOST($prefix.'label', 'alphanohtml')); } if (trim((string) $s->label) === '') { $errors[] = $langs->transnoentities('MahnungStufeLabelFehlt', dol_escape_htmltag($bezeich)); } $s->frist_tage = mahnungSetupTage($prefix.'frist_tage', $s->frist_tage, $bezeich, $langs->transnoentities('MahnungStufeFristTage'), $errors); $s->neue_frist_tage = mahnungSetupTage($prefix.'neue_frist_tage', $s->neue_frist_tage, $bezeich, $langs->transnoentities('MahnungStufeNeueFristTage'), $errors); // Kostenfelder werden IMMER unverändert aus dem Formular übernommen — auch wenn // die Stufe als Zahlungserinnerung markiert ist. Früher wurden sie hier hart auf 0 // geschrieben; ein Fehlklick auf dem Häkchen hat damit Gebühren, Pauschale und // beide Zinssatz-Overrides einer echten Mahnstufe unwiderruflich zerstört. Nötig // war das Nullen ohnehin nie: mahnungBaueVorgang() in lib/mahnung_anlage.lib.php // setzt Gebühr, §288-Pauschale, Zinsen und Vorstufenkosten bei einer Erinnerung // zwingend auf 0, egal was in der Stufenkonfiguration steht. // // Bei einer Zahlungserinnerung blendet das Formular den kompletten KOSTEN-Block // aus — aber nur per display:none, nie per "disabled". Versteckte Felder schickt // der Browser normal mit, deaktivierte gar nicht. Als zweite Sicherung liegt im // Block das versteckte Marker-Feld "kosten_present": kommt es nicht im POST an, // wurde der Block überhaupt nicht gerendert und die gespeicherten Werte bleiben // unangetastet. Ohne diese Klammer würde vor allem das Häkchen der §288-Pauschale // still auf 0 fallen — ein nicht gerendertes Häkchen ist im POST nicht von einem // abgewählten zu unterscheiden. if (GETPOSTISSET($prefix.'kosten_present')) { $s->mahngebuehr_b2c = mahnungSetupBetrag($prefix.'mahngebuehr_b2c', $s->mahngebuehr_b2c, $bezeich, $langs->transnoentities('MahnungStufeGebuehrB2C'), $errors); $s->mahngebuehr_b2b = mahnungSetupBetrag($prefix.'mahngebuehr_b2b', $s->mahngebuehr_b2b, $bezeich, $langs->transnoentities('MahnungStufeGebuehrB2B'), $errors); $s->pauschale_b2b_einmalig = GETPOSTISSET($prefix.'pauschale_b2b_einmalig') ? 1 : 0; $s->zinssatz_b2c_uebersteuern = mahnungSetupZinssatz($prefix.'zinssatz_b2c', $s->zinssatz_b2c_uebersteuern, $bezeich, $langs->transnoentities('MahnungStufeZinssatzB2C'), $errors); $s->zinssatz_b2b_uebersteuern = mahnungSetupZinssatz($prefix.'zinssatz_b2b', $s->zinssatz_b2b_uebersteuern, $bezeich, $langs->transnoentities('MahnungStufeZinssatzB2B'), $errors); } if (GETPOSTISSET($prefix.'versandart')) { $va = GETPOST($prefix.'versandart', 'aZ09'); $s->versandart_default = array_key_exists($va, mahnungSetupVersandarten()) ? $va : 'pdf'; } if (GETPOSTISSET($prefix.'pdf_intro')) { $s->pdf_intro = GETPOST($prefix.'pdf_intro', 'restricthtml'); } // E-Mail-Felder werden je nach Versandart nur ein-/ausgeblendet (display:none), // abgeschickt werden sie trotzdem. Fehlen sie ausnahmsweise doch im POST, bleibt // das gespeicherte Template unangetastet. if (GETPOSTISSET($prefix.'email_subject')) { $s->email_subject = GETPOST($prefix.'email_subject', 'alphanohtml'); } if (GETPOSTISSET($prefix.'email_body')) { $s->email_body = GETPOST($prefix.'email_body', 'restricthtml'); } } /** * Geldbetrag für die Kopfzeile formatieren (deutsche Schreibweise, Euro-Zeichen). * * Das JS am Seitenende formatiert in mahnungGeld() identisch, damit die Kopfzeile * beim Tippen nicht plötzlich anders aussieht als nach dem Neuladen. Wird hier * etwas geändert, muss die JS-Funktion mitgezogen werden. * * @param float $wert Betrag * @return string z. B. "1.234,50 €" */ function mahnungSetupGeld($wert) { return number_format((float) $wert, 2, ',', '.').' €'; } /** * Gebühren-Text der Kopfzeile. Sind B2C- und B2B-Gebühr gleich, steht nur ein * Betrag da; weichen sie ab, werden beide gezeigt (B2C zuerst). * * @param MahnungStufe $s Stufe * @return string */ function mahnungSetupKopfGebuehr($s) { $b2c = (float) $s->mahngebuehr_b2c; $b2b = (float) $s->mahngebuehr_b2b; if (abs($b2b - $b2c) < 0.005) { return mahnungSetupGeld($b2c); } return mahnungSetupGeld($b2c).' / '.mahnungSetupGeld($b2b); } /** * Frist-Text der Kopfzeile: "sofort" bei 0 Tagen, sonst "nach N Tagen". * * @param int $tage Wert aus frist_tage * @return string HTML-kodiert (trans() konvertiert bereits) */ function mahnungSetupKopfFrist($tage) { global $langs; $tage = (int) $tage; if ($tage <= 0) { return $langs->trans('MahnungStufeKopfSofort'); } return $langs->trans('MahnungStufeKopfNachTagen', $tage); } /** * Eine Formularzeile im Stufen-Layout ausgeben (Bezeichnung links, Eingabe rechts). * Bewusst kein : die Stufen stecken in
-Blöcken, und HTML erlaubt kein *
um Tabellenzeilen herum. Das Zwei-Spalten-Raster macht CSS (grid). * * @param string $label Bereits übersetzte Feldbezeichnung * @param string $inputHtml Fertiges HTML des Eingabefelds (Werte dort escapen!) * @param string $cssClass Zusätzliche CSS-Klassen der Zeile * @param string $style Zusätzliches style-Attribut (z. B. "display:none;") * @return void */ function mahnungSetupFeldZeile($label, $inputHtml, $cssClass = '', $style = '') { print '
'; print '
'.$label.'
'; print '
'.$inputHtml.'
'; print '
'; } /** * Zwischenüberschrift eines Blocks (FRISTEN / KOSTEN / VERSAND). * * @param string $text Bereits übersetzte Überschrift * @return void */ function mahnungSetupBlockTitel($text) { print '
'.$text.'
'; } /** * Die zugeklappte Kopfzeile einer Mahnstufe ausgeben ( des Akkordeons). * * Zeigt Stufennummer, Bezeichnung und kompakt die Eckdaten * "Betrag · Versandart · Frist". Bei einer Zahlungserinnerung steht "kostenlos" * statt eines Betrags, zusätzlich gibt es ein Abzeichen. * * In die Kopfzeile kommen bewusst KEINE Bedienelemente: ein Klick auf ein * Eingabefeld oder einen Link innerhalb eines klappt in den Browsern * zusätzlich den Abschnitt um. Aktiv-Häkchen und Löschen sitzen deshalb woanders. * Der Hinweis auf die Löschsperre ist dagegen reiner Text ohne Klickverhalten und * darf mit in die Kopfzeile — dort fließt er im Layout mit, statt sich als * absolut positioniertes Element über die Eckdaten zu legen. * * @param MahnungStufe $s Stufe mit den anzuzeigenden Werten * @param string $key Schlüssel für die CSS-Klassen des Umschalt-JS * @param int $nbUse Anzahl Mahnvorgänge auf dieser Stufennummer (Löschsperre) * @return void */ function mahnungSetupPrintKopf($s, $key, $nbUse = 0) { global $langs; $k = dol_escape_htmltag((string) $key); $istErinnerung = !empty($s->ist_erinnerung); $vaListe = mahnungSetupVersandarten(); $va = $s->versandart_default ?: 'pdf'; $vaText = isset($vaListe[$va]) ? $langs->trans($vaListe[$va]) : $langs->trans('MahnungVersandPdf'); print ''; print ''.((int) $s->stufe).''; print ''.dol_escape_htmltag((string) $s->label).''; // Abzeichen "Zahlungserinnerung" — immer gerendert, per JS ein-/ausgeblendet print ''; // Abzeichen "inaktiv" — ebenfalls immer gerendert, damit das Aktiv-Häkchen im // aufgeklappten Zustand die Kopfzeile ohne Neuladen mitziehen kann print 'active) ? '' : ' style="display:none;"').'>'; print $langs->trans('MahnungStufeInaktiv').''; // Löschsperre: verweist mindestens ein Mahnvorgang auf diese Stufennummer, gibt es // statt des Papierkorbs nur diesen Hinweis. Die vollständige Begründung samt Anzahl // steht im Tooltip. if ($nbUse > 0) { print ''; print $langs->trans('MahnungStufeNichtLoeschbarKurz').''; } print ''; print ''; print $istErinnerung ? $langs->trans('MahnungStufeKopfKostenlos') : dol_escape_htmltag(mahnungSetupKopfGebuehr($s)); print ' · '; print ''.$vaText.' · '; print ''.mahnungSetupKopfFrist($s->frist_tage).''; print ''; print ''; } /** * Die Eingabefelder einer Mahnstufe ausgeben (gemeinsam für Bearbeiten und Anlegen). * * Aufbau: zuerst Bezeichnung, Erinnerungs-Häkchen und Aktiv-Schalter, danach die * drei Blöcke FRISTEN, KOSTEN und VERSAND. Selten gebrauchte Felder (Zinssatz- * Overrides, Textbausteine) stecken in eigenen, zugeklappten Unterabschnitten — * die klappen automatisch auf, sobald darin ein Wert vom Standard abweicht. * * @param MahnungStufe $s Stufe mit den anzuzeigenden Werten * @param string $prefix Feld-Prefix im Formular (z. B. "stufe12_" oder "new_") * @param string $key Schlüssel für die CSS-Klassen des Umschalt-JS (rowid oder "new") * @return void */ function mahnungSetupPrintFelder($s, $prefix, $key) { global $langs; $k = dol_escape_htmltag((string) $key); $istErinnerung = !empty($s->ist_erinnerung); $vaListe = mahnungSetupVersandarten(); $va = $s->versandart_default ?: 'pdf'; if (!isset($vaListe[$va])) { $va = 'pdf'; } // Bei einer Zahlungserinnerung verschwindet der komplette KOSTEN-Block samt // Zwischenüberschrift — aber ausschließlich per display:none, NIE per "disabled". // Versteckte Felder schickt der Browser normal mit, deaktivierte gar nicht: das // Bulk-Update (action save_stufen) würde die gespeicherte Gebührenkonfiguration // sonst beim nächsten Speichern mit Nullen überschreiben, sobald jemand die Stufe // als Erinnerung markiert. Zusätzlich steckt im Block das versteckte Marker-Feld // "kosten_present" — fehlt es im POST, fasst mahnungSetupApplyPost() die // Kostenwerte gar nicht erst an. Die Werte gelten wieder, sobald das Häkchen weg ist. $kostenStyle = $istErinnerung ? ' style="display:none;"' : ''; $hinweisStyle = $istErinnerung ? '' : ' style="display:none;"'; // E-Mail-Felder: sichtbar bei Versandart "E-Mail" ODER wenn die Stufe eine // Zahlungserinnerung ist — die geht laut mahnungBaueVorgang() zwingend per E-Mail // raus, ganz gleich was als Versandart-Default eingestellt ist. Ohne diese // Ausnahme wären Betreff und Text einer Erinnerung nicht erreichbar. $mailStyle = ($va === 'mail' || $istErinnerung) ? '' : 'display:none;'; // Der PDF-Einleitungstext ist das Gegenstück: geht die Stufe per E-Mail raus, // entsteht überhaupt kein Mahnschreiben, in das ein Einleitungstext könnte. // Für eine Zahlungserinnerung gilt das ohnehin — die hat nie ein eigenes PDF, // angehängt wird die unveränderte Original-Rechnung. // Ausgeblendet statt entfernt: der gespeicherte Text bleibt im Formular und // damit erhalten, falls die Versandart später wieder auf Post umgestellt wird. $pdfStyle = ($va === 'mail' || $istErinnerung) ? 'display:none;' : ''; // Standard-Zinssatz für Platzhalter und Hilfetexte (Basiszins + Aufschlag) $basisVal = (float) getDolGlobalString('MAHNUNG_BASISZINS', '1.27'); $aufB2c = (float) getDolGlobalString('MAHNUNG_AUFSCHLAG_B2C', '5.0'); $aufB2b = (float) getDolGlobalString('MAHNUNG_AUFSCHLAG_B2B', '9.0'); $stdB2c = $basisVal + $aufB2c; $stdB2b = $basisVal + $aufB2b; $zinsB2c = ($s->zinssatz_b2c_uebersteuern !== null && $s->zinssatz_b2c_uebersteuern !== '') ? (string) (float) $s->zinssatz_b2c_uebersteuern : ''; $zinsB2b = ($s->zinssatz_b2b_uebersteuern !== null && $s->zinssatz_b2b_uebersteuern !== '') ? (string) (float) $s->zinssatz_b2b_uebersteuern : ''; // Unterabschnitte starten zugeklappt — außer es steckt bereits etwas darin, das // vom Standard abweicht. Sonst findet man seine eigenen Einstellungen nicht wieder. $zinsOffen = ($zinsB2c !== '' || $zinsB2b !== ''); $texteOffen = (trim((string) $s->pdf_intro) !== '' || trim((string) $s->email_subject) !== '' || trim((string) $s->email_body) !== ''); // --- Bezeichnung, Erinnerungs-Häkchen, Aktiv-Schalter ----------------------- // Das "required" bleibt am Bezeichnungsfeld: Damit die Browser-Meldung nicht an // einem zugeklappten
verschluckt wird (Chrome sendet dann kommentarlos // nicht ab), klappt das JS am Seitenende beim invalid-Ereignis alle übergeordneten // Abschnitte auf. Serverseitig prüft mahnungSetupApplyPost() zusätzlich. mahnungSetupFeldZeile( $langs->trans('MahnungStufeLabel'), '' ); mahnungSetupFeldZeile( $langs->trans('MahnungStufeIstErinnerung'), ' ' .''.$langs->trans('MahnungStufeIstErinnerungHelp').'' ); mahnungSetupFeldZeile( $langs->trans('Active'), 'active) ? ' checked' : '').'>' ); // --- Block FRISTEN ---------------------------------------------------------- mahnungSetupBlockTitel($langs->trans('MahnungBlockFristen')); mahnungSetupFeldZeile( $langs->trans('MahnungStufeFristTage'), '' ); mahnungSetupFeldZeile( $langs->trans('MahnungStufeNeueFristTage'), '' ); // --- Block KOSTEN (bei einer Zahlungserinnerung komplett ausgeblendet) ------- print '
'; print ''; mahnungSetupBlockTitel($langs->trans('MahnungBlockKosten')); mahnungSetupFeldZeile( $langs->trans('MahnungStufeGebuehrB2C'), ' EUR' ); mahnungSetupFeldZeile( $langs->trans('MahnungStufeGebuehrB2B'), ' EUR' ); mahnungSetupFeldZeile( $langs->trans('MahnungPauschaleB2B').' (§288 Abs. 5)', 'pauschale_b2b_einmalig) ? ' checked' : '').'>' ); // Zinssatz-Overrides: selten gebraucht, deshalb hinter einem eigenen Abschnitt. // Ist bereits ein Override gesetzt, startet er aufgeklappt. print '
'; print ''.$langs->trans('MahnungBlockZinssatzOverride').''; print '
'; mahnungSetupFeldZeile( $langs->trans('MahnungStufeZinssatzB2C'), ' %' .' '.$langs->trans('MahnungZinssatzHelpB2C', number_format($basisVal, 2, ',', '.'), number_format($aufB2c, 1, ',', '.'), number_format($stdB2c, 2, ',', '.')).'' ); mahnungSetupFeldZeile( $langs->trans('MahnungStufeZinssatzB2B'), ' %' .' '.$langs->trans('MahnungZinssatzHelpB2B', number_format($basisVal, 2, ',', '.'), number_format($aufB2b, 1, ',', '.'), number_format($stdB2b, 2, ',', '.')).'' ); print '
'; print '
'; print '
'; // Ersatz für den ausgeblendeten Kostenblock: eine Zeile, dass die Stufe nichts kostet print '
'; mahnungSetupBlockTitel($langs->trans('MahnungBlockKosten')); print '
'.$langs->trans('MahnungStufeErinnerungKostenHinweis').'
'; print '
'; // --- Block VERSAND ---------------------------------------------------------- mahnungSetupBlockTitel($langs->trans('MahnungBlockVersand')); $versandHtml = ''; // Hinweis immer rendern, aber nur zeigen wenn Erinnerung + Versandart != E-Mail. // Das JS schaltet ihn beim Umstellen live um — sonst passte der Hinweis nach dem // Anhaken des Erinnerungs-Häkchens erst nach einem Neuladen wieder zur Auswahl. $versandHtml .= ' '; mahnungSetupFeldZeile($langs->trans('MahnungStufeVersandartDefault'), $versandHtml); // Textbausteine: ebenfalls selten angefasst, deshalb zugeklappt. Ist bereits ein // Text hinterlegt, startet der Abschnitt offen. print '
'; print ''.$langs->trans('MahnungBlockTexte').''; print '
'; mahnungSetupFeldZeile( $langs->trans('MahnungStufePdfIntro'), '', 'mahnung_pdfrow_'.$k, $pdfStyle ); // Welche Platzhalter es gibt, stand bisher nur als Kommentar in der Sprachdatei — // im Formular war es Ratesache. Beide Schreibweisen werden unterstützt. mahnungSetupFeldZeile( '', ''.$langs->trans('MahnungPlatzhalterHilfe').'', 'mahnung_emailrow_'.$k, $mailStyle ); // E-Mail-Felder bei Versandart "E-Mail" oder bei einer Zahlungserinnerung // (Umschalten ohne Neuladen per JS unten — siehe syncEmailRows()). mahnungSetupFeldZeile( $langs->trans('MahnungStufeEmailSubject'), '', 'mahnung_emailrow_'.$k, $mailStyle ); // Mailtext mit dem gewohnten Dolibarr-Editor pflegen (WYSIWYG, wenn // FCKEDITOR_ENABLE_MAIL gesetzt ist — dieselbe Konstante und dieselbe // Toolbar wie bei Dolibarrs eigenen Mailvorlagen, siehe // admin/mails_templates.php). Wer lieber Klartext schreibt, bekommt bei // abgeschalteter Konstante weiterhin ein einfaches Textfeld. $editorEmailBody = new DolEditor( $prefix.'email_body', (string) $s->email_body, '', 220, 'dolibarr_mailings', 'In', false, true, (bool) getDolGlobalString('FCKEDITOR_ENABLE_MAIL'), ROWS_5, '90%' ); mahnungSetupFeldZeile( $langs->trans('MahnungStufeEmailBody'), $editorEmailBody->Create(1), 'mahnung_emailrow_'.$k, $mailStyle ); print '
'; print '
'; } // --------------------------------------------------------------- // POST: Allgemeine Konstanten speichern // --------------------------------------------------------------- if ($action === 'save_consts' && $user->hasRight('mahnung', 'setup')) { $basis = str_replace(',', '.', GETPOST('MAHNUNG_BASISZINS', 'alphanohtml')); $b2c = str_replace(',', '.', GETPOST('MAHNUNG_AUFSCHLAG_B2C', 'alphanohtml')); $b2b = str_replace(',', '.', GETPOST('MAHNUNG_AUFSCHLAG_B2B', 'alphanohtml')); $pau = str_replace(',', '.', GETPOST('MAHNUNG_PAUSCHALE_B2B', 'alphanohtml')); $topic = GETPOST('MAHNUNG_NTFY_TOPIC', 'alphanohtml'); $boxmax = (int) GETPOST('MAHNUNG_BOX_MAXLINES', 'int'); // Absender der Erinnerungs-Mails. Leer = Firmenadresse bzw. globale // Dolibarr-Absenderadresse (siehe mahnungAbsender()). $senderMail = trim((string) GETPOST('MAHNUNG_EMAIL_SENDER', 'alphanohtml')); $senderName = trim((string) GETPOST('MAHNUNG_EMAIL_SENDER_NAME', 'alphanohtml')); if ($senderMail !== '' && !isValidEmail($senderMail)) { // Eine ungültige Absenderadresse würde jeden Versand scheitern lassen — // lieber gar nicht erst speichern. setEventMessages($langs->trans('MahnungSenderMailUngueltig', $senderMail), null, 'errors'); header('Location: '.$_SERVER['PHP_SELF']); exit; } dolibarr_set_const($db, 'MAHNUNG_BASISZINS', (string) (float) $basis, 'chaine', 0, '', 0); dolibarr_set_const($db, 'MAHNUNG_AUFSCHLAG_B2C', (string) (float) $b2c, 'chaine', 0, '', 0); dolibarr_set_const($db, 'MAHNUNG_AUFSCHLAG_B2B', (string) (float) $b2b, 'chaine', 0, '', 0); dolibarr_set_const($db, 'MAHNUNG_PAUSCHALE_B2B', (string) (float) $pau, 'chaine', 0, '', 0); dolibarr_set_const($db, 'MAHNUNG_NTFY_TOPIC', (string) $topic, 'chaine', 0, '', $conf->entity); // Widget-Anzeige: max. Zeilen offener Rechnungen (0 = alle) dolibarr_set_const($db, 'MAHNUNG_BOX_MAXLINES', (string) $boxmax, 'chaine', 0, '', 0); dolibarr_set_const($db, 'MAHNUNG_EMAIL_SENDER', $senderMail, 'chaine', 0, '', $conf->entity); dolibarr_set_const($db, 'MAHNUNG_EMAIL_SENDER_NAME', $senderName, 'chaine', 0, '', $conf->entity); setEventMessages($langs->trans('MahnungSettingsSaved'), null, 'mesgs'); header('Location: '.$_SERVER['PHP_SELF']); exit; } // --------------------------------------------------------------- // POST: Stufen speichern (Bulk-Update über alle konfigurierten Stufen) // --------------------------------------------------------------- if ($action === 'save_stufen' && $user->hasRight('mahnung', 'setup')) { $errors = array(); $toSave = array(); foreach (mahnungSetupAlleStufen($db, $conf->entity) as $s) { $prefix = 'stufe'.((int) $s->id).'_'; // Block war gar nicht im Formular (z. B. Stufe parallel angelegt) -> nicht anfassen if (!GETPOSTISSET($prefix.'present')) { continue; } mahnungSetupApplyPost($s, $prefix, (string) $s->stufe, $errors); $toSave[(int) $s->id] = $s; } if (!empty($errors)) { // Nichts speichern — die eingegebenen Werte bleiben im Formular stehen setEventMessages('', $errors, 'errors'); $stufenPost = $toSave; } else { $ok = true; foreach ($toSave as $s) { if ($s->update($user) <= 0) { $ok = false; setEventMessages($s->error ?: $langs->trans('MahnungSpeichernFehlgeschlagen'), null, 'errors'); } } if ($ok) { setEventMessages($langs->trans('MahnungSettingsSaved'), null, 'mesgs'); header('Location: '.$_SERVER['PHP_SELF']); exit; } $stufenPost = $toSave; } } // --------------------------------------------------------------- // POST: Neue Mahnstufe anlegen // --------------------------------------------------------------- if ($action === 'add_stufe' && $user->hasRight('mahnung', 'setup')) { $errors = array(); $neu = new MahnungStufe($db); $neu->entity = $conf->entity; // Stufennummer: frei wählbar, aber TINYINT -> 0..127 $rawStufe = trim((string) GETPOST('new_stufe', 'alphanohtml')); if (!preg_match('/^\d+$/', $rawStufe) || (int) $rawStufe > 127) { $errors[] = $langs->transnoentities('MahnungStufeNummerUngueltig'); $neu->stufe = 0; } else { $neu->stufe = (int) $rawStufe; } mahnungSetupApplyPost($neu, 'new_', ($rawStufe !== '' ? $rawStufe : '?'), $errors); if (!empty($errors)) { setEventMessages('', $errors, 'errors'); $neuStufePost = $neu; } else { $resCreate = $neu->create($user); if ($resCreate > 0) { setEventMessages($langs->trans('MahnungStufeAngelegt'), null, 'mesgs'); header('Location: '.$_SERVER['PHP_SELF']); exit; } // -2 = Stufennummer bereits vergeben (MahnungStufeNummerVergeben) setEventMessages($neu->error ?: $langs->trans('MahnungSpeichernFehlgeschlagen'), null, 'errors'); $neuStufePost = $neu; } } // --------------------------------------------------------------- // POST: Mahnstufe löschen (bestätigt über den formconfirm-Dialog weiter unten) // --------------------------------------------------------------- if ($action === 'confirm_delete_stufe' && $confirm === 'yes' && $rowid > 0 && $user->hasRight('mahnung', 'setup')) { $del = new MahnungStufe($db); if ($del->fetch($rowid) <= 0) { setEventMessages($langs->trans('MahnungStufeNichtGefunden'), null, 'errors'); header('Location: '.$_SERVER['PHP_SELF']); exit; } // Löschsperre serverseitig: verweist auch nur ein Mahnvorgang auf diese // Stufennummer, darf die Konfiguration nicht verschwinden — sonst verlieren // Bestandsmahnungen ihre Grundlage. Dann ist "deaktivieren" der richtige Weg. $nbUse = mahnungSetupZaehleMahnungen($db, (int) $del->stufe); if ($nbUse < 0) { setEventMessages($langs->trans('MahnungStufePruefungFehlgeschlagen'), null, 'errors'); header('Location: '.$_SERVER['PHP_SELF']); exit; } if ($nbUse > 0) { setEventMessages($langs->transnoentities('MahnungStufeNichtLoeschbarAnzahl', (int) $del->stufe, $nbUse), null, 'errors'); header('Location: '.$_SERVER['PHP_SELF']); exit; } if ($del->delete($user) > 0) { setEventMessages($langs->trans('MahnungStufeGeloescht'), null, 'mesgs'); } else { setEventMessages($del->error ?: $langs->trans('MahnungSpeichernFehlgeschlagen'), null, 'errors'); } header('Location: '.$_SERVER['PHP_SELF']); exit; } // --------------------------------------------------------------- // ODT-Upload / Template-Löschen (actions_setmoduleoptions.inc.php) // Muss vor llxHeader() stehen, da es header()-Redirects macht. // --------------------------------------------------------------- include_once DOL_DOCUMENT_ROOT.'/core/actions_setmoduleoptions.inc.php'; // --------------------------------------------------------------- // Dokumentenmodell-Aktionen (vor View, da header()-Redirect) // --------------------------------------------------------------- if ($action === 'setdoc' && $user->hasRight('mahnung', 'setup')) { $newModel = GETPOST('value', 'alphanohtml'); if (!empty($newModel)) { dolibarr_set_const($db, 'MAHNUNG_ADDON_PDF', $newModel, 'chaine', 0, '', $conf->entity); } header('Location: '.$_SERVER['PHP_SELF']); exit; } if ($action === 'set' && $user->hasRight('mahnung', 'setup')) { $newModel = GETPOST('value', 'alphanohtml'); $sql = "INSERT INTO ".MAIN_DB_PREFIX."document_model (nom, type, entity, libelle) VALUES ('".$db->escape($newModel)."', 'mahnung', ".((int) $conf->entity).", '".$db->escape($newModel)."')"; $db->query($sql); header('Location: '.$_SERVER['PHP_SELF']); exit; } if ($action === 'del' && $user->hasRight('mahnung', 'setup')) { $newModel = GETPOST('value', 'alphanohtml'); $sql = "DELETE FROM ".MAIN_DB_PREFIX."document_model WHERE nom = '".$db->escape($newModel)."' AND type = 'mahnung' AND entity = ".((int) $conf->entity); $db->query($sql); if (getDolGlobalString('MAHNUNG_ADDON_PDF') === $newModel) { dolibarr_del_const($db, 'MAHNUNG_ADDON_PDF', $conf->entity); } header('Location: '.$_SERVER['PHP_SELF']); exit; } // --------------------------------------------------------------- // View // --------------------------------------------------------------- llxHeader('', $langs->trans('MahnungSetupPage')); $setupExtra = ''.dol_escape_htmltag($langs->trans('MahnungTrackingPatternsSetup')).''; print load_fiche_titre($langs->trans('MahnungSetupPage'), $setupExtra, 'fa-envelope-open-text'); print ''.$langs->trans('MahnungSetupDescription').'

'; // --- Block: Konstanten ------------------------------------------------------- print '
'; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; // Widget: max. Anzahl angezeigter offener Rechnungen (0 = alle) $curBoxMax = getDolGlobalInt('MAHNUNG_BOX_MAXLINES', 0); print ''; print ''; // Absender der Erinnerungs-Mails. Leer lassen = Firmenadresse aus Start > Einstellungen // > Firma/Stiftung, sonst die globale Dolibarr-Absenderadresse. print ''; print ''; print ''; print ''; print '
'.$langs->trans('MahnungSetup').'
'.$langs->trans('MahnungBasiszins').' %'; print ' '.$langs->trans('MahnungBasiszinsHelp').'
'.$langs->trans('MahnungAufschlagB2C').' %
'.$langs->trans('MahnungAufschlagB2B').' %
'.$langs->trans('MahnungPauschaleB2BLabel').' EUR
'.$langs->trans('MahnungNtfyTopic').''; print ' '.$langs->trans('MahnungNtfyTopicHelp').'
'.$langs->trans('MahnungBoxMaxLines').''; print ' '.$langs->trans('MahnungBoxMaxLinesHelp').'
'.$langs->trans('MahnungSenderMail').''; print ' '.$langs->trans('MahnungSenderMailHelp').'
'.$langs->trans('MahnungSenderName').''; print ' '.$langs->trans('MahnungSenderNameHelp').'
'; print '
'; print '
'; // --- Block: Stufen ----------------------------------------------------------- // Lokales CSS (kein CDN) für das Mahnstufen-Akkordeon. Bewusst ohne feste // Theme-Farben: Hintergründe sind neutrale Grau-Schleier mit Alpha, Rahmen und // Pfeil nutzen currentColor, die Schrift erbt die Farbe des Themes. Damit bleibt // alles sowohl im hellen Standard-Theme als auch im dunklen awl-dark lesbar. // Die Dolibarr-Klasse "badge" liefert nur die Grundform — Farbe und Hintergrund // setzt "mahnung_badge" bewusst neu, weil Dolibarrs Badge-Varianten weißen Text // mitbringen, der auf einem hellen Untergrund verschwinden würde. print ''; $stufen = mahnungSetupAlleStufen($db, $conf->entity); // Nach fehlgeschlagener Validierung die eingegebenen Werte weiterverwenden foreach ($stufen as $idx => $s) { if (isset($stufenPost[(int) $s->id])) { $stufen[$idx] = $stufenPost[(int) $s->id]; } } $belegung = mahnungSetupStufenBelegung($db); // Löschen-Bestätigung (Dolibarr-Dialog). Der 7. Parameter ist $useajax=1, das "Ja" // ruft deshalb per GET setup.php?rowid=X&action=confirm_delete_stufe&confirm=yes&token=… // auf — das Token hängt der Dialog selbst an, geprüft wird es oben im CSRF-Wächter. if ($action === 'ask_delete_stufe' && $rowid > 0 && $user->hasRight('mahnung', 'setup')) { $askDel = new MahnungStufe($db); if ($askDel->fetch($rowid) > 0) { print $form->formconfirm( $_SERVER['PHP_SELF'].'?rowid='.((int) $askDel->id), $langs->trans('MahnungStufeLoeschenTitel'), $langs->transnoentities('MahnungStufeLoeschenFrage', (int) $askDel->stufe, dol_escape_htmltag((string) $askDel->label)), 'confirm_delete_stufe', '', 0, 1 ); } } print '

'; print load_fiche_titre($langs->trans('MahnungStufen'), '', ''); print '
'.$langs->trans('MahnungStufenIntro').'
'; print '
'.$langs->trans('MahnungStufenAkkordeonHilfe').'

'; print '
'; print ''; print ''; if (empty($stufen)) { print '
'.$langs->trans('MahnungStufeKeine').'
'; } foreach ($stufen as $s) { $key = (int) $s->id; $prefix = 'stufe'.$key.'_'; $nbUse = isset($belegung[(int) $s->stufe]) ? (int) $belegung[(int) $s->stufe] : 0; // Alle Stufen starten zugeklappt, damit man sie auf einen Blick hat. Ausnahme: // nach einer fehlgeschlagenen Validierung wird die beanstandete Stufe aufgeklappt // gezeigt — sonst sucht man die zurückgewiesene Eingabe hinter einer Kopfzeile. $offen = isset($stufenPost[$key]); print '
'; print '
'; mahnungSetupPrintKopf($s, (string) $key, $nbUse); print '
'; print ''; mahnungSetupPrintFelder($s, $prefix, (string) $key); print '
'; print '
'; // Löschen nur, wenn kein Mahnvorgang auf die Stufennummer verweist (Löschsperre; // serverseitig noch einmal geprüft in der Action confirm_delete_stufe). Der Link // sitzt bewusst neben dem und nicht darin: ein Klick auf einen Link // innerhalb eines würde zusätzlich den Abschnitt auf- oder zuklappen. // Ist die Stufe gesperrt, steht der Hinweis dafür in der Kopfzeile. if ($nbUse === 0) { print ''; print ''; print img_picto($langs->trans('Delete'), 'delete'); print ''; print ''; } print '
'; } print '
'; print '
'; // --- Block: Neue Stufe anlegen ---------------------------------------------- $neuVorlage = $neuStufePost; if ($neuVorlage === null) { $stufeHelper = new MahnungStufe($db); $neuVorlage = new MahnungStufe($db); $neuVorlage->stufe = $stufeHelper->naechsteFreieStufe(); $neuVorlage->label = ''; $neuVorlage->frist_tage = 0; $neuVorlage->neue_frist_tage = 7; $neuVorlage->versandart_default = 'pdf'; $neuVorlage->active = 1; } print '
'; print load_fiche_titre($langs->trans('MahnungStufeNeu'), '', ''); print '
'; print ''; print ''; // Das Anlege-Formular bleibt bewusst offen (kein Akkordeon): es gibt nur eines und // wer hier landet, will gerade eine Stufe anlegen. Das Aktiv-Häkchen steckt in // mahnungSetupPrintFelder() — es darf pro Formular nur einmal vorkommen, sonst // entscheidet die Reihenfolge im POST über den gespeicherten Wert. print '
'; mahnungSetupFeldZeile( $langs->trans('MahnungStufeNummer'), ' ' .''.$langs->trans('MahnungStufeNummerHelp').'' ); mahnungSetupPrintFelder($neuVorlage, 'new_', 'new'); print '
'; print '
'; print '
'; // Textbausteine für die Kopfzeilen-Aktualisierung im Browser. transnoentities() // liefert die Rohfassung ohne HTML-Entities — für JSON ist das richtig, die // JSON_HEX_*-Flags entschärfen alles, was den '; // --- Block: Dokumentenmodelle ----------------------------------------------------------- print '

'; print load_fiche_titre($langs->trans('MahnungDokumentModelle'), ''.$langs->trans('MahnungSetupTemplateVars').'', ''); // Dokumentenmodelle auflisten $def = array(); $sql = "SELECT nom FROM ".MAIN_DB_PREFIX."document_model WHERE type = 'mahnung' AND entity = ".((int) $conf->entity); $resql = $db->query($sql); if ($resql) { while ($obj = $db->fetch_object($resql)) { $def[] = $obj->nom; } $db->free($resql); } // Verfügbare Modelle scannen (aus den doc/-Klassen) $dirmodels = array( DOL_DOCUMENT_ROOT.'/custom/mahnung/core/modules/mahnung/doc/', ); $modellist = array(); foreach ($dirmodels as $dmod) { $files = dol_dir_list($dmod, 'files', 0, '\.modules\.php$'); foreach ($files as $file) { $classname = preg_replace('/\.modules\.php$/', '', $file['name']); // Modellname ohne doc_/pdf_-Prefix (commonGenerateDocument fuegt den Prefix selbst hinzu) $modelname = preg_replace('/^(doc|pdf)_/', '', $classname); include_once $file['fullname']; if (class_exists($classname)) { $obj = new $classname($db); $modellist[$modelname] = $obj; } } } print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; foreach ($modellist as $mname => $mobj) { print ''; print ''; print ''; // Status (aktiviert/deaktiviert) print ''; // Default print ''; // Info (ODT: Upload-Formular) print ''; print ''; } print '
'.$langs->trans('Name').''.$langs->trans('Description').''.$langs->trans('Status').''.$langs->trans('Default').'
'.dol_escape_htmltag($mobj->name).''.dol_escape_htmltag($mobj->description ?? '').''; if (in_array($mname, $def)) { print ''; print img_picto($langs->trans('Activated'), 'switch_on'); print ''; } else { print ''; print img_picto($langs->trans('Disabled'), 'switch_off'); print ''; } print ''; if (getDolGlobalString('MAHNUNG_ADDON_PDF') === $mname) { print img_picto($langs->trans('Default'), 'on'); } elseif (in_array($mname, $def)) { print ''; print img_picto($langs->trans('SetAsDefault'), 'off'); print ''; } print ''; if (method_exists($mobj, 'info')) { print $mobj->info($langs); } print '
'; llxFooter(); $db->close();