*
* 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/card.php
* \ingroup mahnung
* \brief Detailansicht eines einzelnen Mahnvorgangs.
*/
$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) {
die("Include of main fails");
}
require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/class/mahnung.class.php';
require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
global $langs, $user, $db;
$langs->loadLangs(array('mahnung@mahnung', 'companies', 'bills'));
if (!$user->hasRight('mahnung', 'read')) {
accessforbidden();
}
$id = GETPOSTINT('id');
$action = GETPOST('action', 'aZ09');
$mahnung = new Mahnung($db);
if ($mahnung->fetch($id) <= 0) {
dol_print_error($db, 'Mahnung nicht gefunden');
exit;
}
// Stornieren
if ($action === 'storno' && $user->hasRight('mahnung', 'delete')) {
$mahnung->status = Mahnung::STATUS_STORNIERT;
if ($mahnung->update($user) > 0) {
setEventMessages($langs->trans('MahnungStornieren').' OK', null, 'mesgs');
header('Location: '.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id));
exit;
}
setEventMessages($mahnung->error, null, 'errors');
}
// Dokument neu generieren
if ($action === 'regenerate_pdf' && $user->hasRight('mahnung', 'write')) {
$selectedModel = GETPOST('model', 'alphanohtml');
$result = $mahnung->generateDocument($selectedModel, $langs);
if ($result > 0) {
setEventMessages('Dokument erstellt', null, 'mesgs');
} else {
setEventMessages('Dokument-Fehler: '.$mahnung->error, null, 'errors');
}
header('Location: '.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id));
exit;
}
// Versand-Daten speichern (Datum, Weg, optional Tracking)
if ($action === 'set_versand' && $user->hasRight('mahnung', 'write')) {
$y = GETPOSTINT('versand_year');
$m = GETPOSTINT('versand_month');
$d = GETPOSTINT('versand_day');
$dateVersand = ($y && $m && $d) ? dol_mktime(12, 0, 0, $m, $d, $y) : dol_now();
$weg = GETPOST('versandweg', 'aZ09');
$trackNr = trim((string) GETPOST('tracking_nr', 'alphanohtml'));
$trackProv = GETPOST('tracking_provider', 'aZ09');
if (empty($trackProv) && $trackNr !== '') {
$trackProv = Mahnung::defaultProviderForWeg($weg);
}
if ($mahnung->setVersand($user, $dateVersand, $weg, $trackNr ?: null, $trackProv ?: null) > 0) {
setEventMessages($langs->trans('MahnungVersandGespeichert'), null, 'mesgs');
} else {
setEventMessages($mahnung->error ?: 'Fehler beim Speichern', null, 'errors');
}
header('Location: '.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id));
exit;
}
// Sendungsnummer aus erkanntem Vorschlag uebernehmen
if ($action === 'apply_tracking' && $user->hasRight('mahnung', 'write')) {
$nr = trim((string) GETPOST('nr', 'alphanohtml'));
$prov = GETPOST('provider', 'aZ09');
if ($nr !== '' && $prov !== '') {
$mahnung->tracking_nr = $nr;
$mahnung->tracking_provider = $prov;
if ($mahnung->update($user) > 0) {
setEventMessages($langs->trans('MahnungTrackingUebernommen'), null, 'mesgs');
}
}
// Vorschlag aus Session entfernen
unset($_SESSION['mahnung_tracking_suggestions_'.((int) $mahnung->id)]);
header('Location: '.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id));
exit;
}
// Vorschlaege verwerfen
if ($action === 'dismiss_tracking' && $user->hasRight('mahnung', 'write')) {
unset($_SESSION['mahnung_tracking_suggestions_'.((int) $mahnung->id)]);
header('Location: '.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id));
exit;
}
// Belege scannen: pdftotext + Pattern-Matching
if ($action === 'scan_belege' && $user->hasRight('mahnung', 'write')) {
require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/class/mahnungtrackingpattern.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
$mahnungSafeRef = dol_sanitizeFileName($mahnung->ref);
$scanDir = (!empty($conf->mahnung->multidir_output[$mahnung->entity])
? $conf->mahnung->multidir_output[$mahnung->entity]
: ($conf->mahnung->dir_output ?? (DOL_DATA_ROOT.'/mahnung')))
.'/'.$mahnungSafeRef;
$patternService = new MahnungTrackingPattern($db);
$suggestions = array();
$pdftotextAvailable = null; // einmalig pruefen
if (is_dir($scanDir)) {
foreach (dol_dir_list($scanDir, 'files', 0) as $file) {
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
$text = '';
if ($ext === 'txt' || $ext === 'html' || $ext === 'htm') {
$text = @file_get_contents($file['fullname']);
if ($ext === 'html' || $ext === 'htm') {
$text = strip_tags((string) $text);
}
} elseif ($ext === 'pdf') {
if ($pdftotextAvailable === null) {
$check = @shell_exec('command -v pdftotext 2>/dev/null');
$pdftotextAvailable = !empty(trim((string) $check));
}
if ($pdftotextAvailable) {
$cmd = 'pdftotext -layout '.escapeshellarg($file['fullname']).' - 2>/dev/null';
$text = (string) @shell_exec($cmd);
}
}
if ($text === '') {
continue;
}
$hit = $patternService->detectFromText($text);
if ($hit !== null) {
$suggestions[] = array(
'file' => $file['name'],
'provider' => $hit['provider'],
'nr' => $hit['nr'],
'url' => $hit['url'],
'label' => $hit['label'],
);
}
}
}
$_SESSION['mahnung_tracking_suggestions_'.((int) $mahnung->id)] = $suggestions;
if ($pdftotextAvailable === false) {
setEventMessages($langs->trans('MahnungPdftotextMissing'), null, 'warnings');
}
header('Location: '.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id));
exit;
}
// Versand-Daten zuruecksetzen (z.B. Korrekturmoeglichkeit)
if ($action === 'clear_versand' && $user->hasRight('mahnung', 'write')) {
$mahnung->date_versand = null;
$mahnung->versandweg = null;
$mahnung->tracking_nr = null;
$mahnung->tracking_provider = null;
// Status nicht zurueckdrehen — nur Daten loeschen
if ($mahnung->update($user) > 0) {
setEventMessages($langs->trans('MahnungVersandGeleert'), null, 'mesgs');
}
header('Location: '.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id));
exit;
}
llxHeader('', $langs->trans('MahnungRef').' '.$mahnung->ref);
print load_fiche_titre($langs->trans('MahnungRef').' '.$mahnung->ref, '', 'fa-envelope-open-text');
print '
';
print '| '.$langs->trans('MahnungRef').' | '.dol_escape_htmltag($mahnung->ref).' |
';
print '| '.$langs->trans('MahnungStufe').' | '.((int) $mahnung->stufe).' |
';
print '| '.$langs->trans('MahnungDatum').' | '.dol_print_date($mahnung->date_mahnung, 'day').' |
';
print '| '.$langs->trans('MahnungFaelligkeitAlt').' | '.dol_print_date($mahnung->date_lim_reglement_alt, 'day').' |
';
print '| '.$langs->trans('MahnungFaelligkeitNeu').' | '.dol_print_date($mahnung->date_lim_reglement_neu, 'day').' |
';
// Rechnung
$facture = new Facture($db);
if ($facture->fetch((int) $mahnung->fk_facture) > 0) {
print '| '.$langs->trans('MahnungRechnung').' | ';
print ''.dol_escape_htmltag($facture->ref).' |
';
}
// Kunde
$societe = new Societe($db);
if ($societe->fetch((int) $mahnung->fk_soc) > 0) {
print '| '.$langs->trans('MahnungKunde').' | ';
print ''.dol_escape_htmltag($societe->name).' ('.dol_escape_htmltag((string) $mahnung->customertype).') |
';
}
print '| '.$langs->trans('MahnungBetragOffen').' | '.price($mahnung->betrag_offen).' |
';
print '| '.$langs->trans('MahnungGebuehr').' | '.price($mahnung->mahngebuehr).' |
';
if ((float) $mahnung->pauschale_b2b > 0) {
print '| '.$langs->trans('MahnungPauschaleB2B').' | '.price($mahnung->pauschale_b2b).' |
';
}
print '| '.$langs->trans('MahnungVerzugszinsen').' | '.price($mahnung->verzugszinsen).' (Basiszins '.number_format((float) $mahnung->basiszins_snapshot, 2, ',', '.').' %) |
';
print '| '.$langs->trans('MahnungSumme').' | '.price($mahnung->summe_mahnung).' |
';
print '| Status | '.dol_escape_htmltag($mahnung->getStatusLabel()).' |
';
print '
';
// --- Generierte Dokumente (wie bei Rechnungen) ---
print '
';
print load_fiche_titre($langs->trans('Documents'), '', 'fa-file');
// Dokumente im Rechnungsordner suchen die zur Mahnung gehoeren
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
$docDir = '';
if ($facture->id > 0) {
$docDir = !empty($conf->facture->multidir_output[$facture->entity])
? $conf->facture->multidir_output[$facture->entity]
: $conf->facture->dir_output;
$docDir .= '/'.dol_sanitizeFileName($facture->ref);
}
$mahnungRef = dol_sanitizeFileName($mahnung->ref);
$fileList = array();
if (!empty($docDir) && is_dir($docDir)) {
$allFiles = dol_dir_list($docDir, 'files', 0, preg_quote($mahnungRef, '/'));
foreach ($allFiles as $f) {
$fileList[] = $f;
}
}
if (!empty($fileList)) {
print '';
print '';
print '| '.$langs->trans('Document').' | ';
print ''.$langs->trans('Size').' | ';
print ''.$langs->trans('Date').' | ';
print ' | ';
print '
';
foreach ($fileList as $f) {
$fname = $f['name'];
$relativePath = dol_sanitizeFileName($facture->ref).'/'.$fname;
$dlUrl = DOL_URL_ROOT.'/document.php?modulepart=facture&file='.urlencode($relativePath);
$viewUrl = $dlUrl.'&attachment=0'; // Inline-Ansicht (keine Download-Erzwingung)
$ext = strtolower(pathinfo($fname, PATHINFO_EXTENSION));
$icon = ($ext === 'pdf') ? 'pdf' : (($ext === 'odt') ? 'ooffice' : 'generic');
$filesize = !empty($f['size']) ? $f['size'] : filesize($f['fullname']);
$filedate = !empty($f['date']) ? $f['date'] : filemtime($f['fullname']);
print '';
// Dateiname mit Icon
print '| ';
print '';
print img_picto('', $icon, 'class="pictofixedwidth"');
print dol_escape_htmltag($fname);
print '';
print ' | ';
// Groesse
print ''.dol_print_size($filesize, 0, 0).' | ';
// Datum
print ''.dol_print_date($filedate, 'dayhour').' | ';
// Aktionen: Vorschau + Download
print '';
if ($ext === 'pdf') {
print ''.img_picto($langs->trans('Preview'), 'search', 'class="pictofixedwidth"').' ';
}
print ''.img_picto($langs->trans('Download'), 'download', 'class="pictofixedwidth"').'';
print ' | ';
print '
';
}
print '
';
} else {
print ''.$langs->trans('NoDocuments').'
';
}
// Aktionen
require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
$form = new Form($db);
$formfile = new FormFile($db);
// --- Versand & Belege ---
print '
';
print load_fiche_titre($langs->trans('MahnungVersandBelege'), '', 'fa-truck');
// Versandwege (Dropdown-Optionen, Label kommt aus Lang-File MahnungVersandweg*)
$versandwege = array(
'post' => $langs->trans('MahnungVersandwegPost'),
'einschreiben' => $langs->trans('MahnungVersandwegEinschreiben'),
'dhl' => $langs->trans('MahnungVersandwegDhl'),
'dpd' => $langs->trans('MahnungVersandwegDpd'),
'hermes' => $langs->trans('MahnungVersandwegHermes'),
'ups' => $langs->trans('MahnungVersandwegUps'),
'fax' => $langs->trans('MahnungVersandwegFax'),
'email' => $langs->trans('MahnungVersandwegEmail'),
'persoenlich' => $langs->trans('MahnungVersandwegPersoenlich'),
'eigen' => $langs->trans('MahnungVersandwegEigen'),
);
$editVersand = ($action === 'edit_versand') || empty($mahnung->date_versand);
$canWrite = $user->hasRight('mahnung', 'write');
if (!empty($mahnung->date_versand) && $action !== 'edit_versand') {
// Anzeige der bereits erfassten Versanddaten
print '';
print '| '.$langs->trans('MahnungVersanddatum').' | '.dol_print_date($mahnung->date_versand, 'day').' |
';
print '| '.$langs->trans('MahnungVersandweg').' | '
.($mahnung->versandweg && isset($versandwege[$mahnung->versandweg]) ? dol_escape_htmltag($versandwege[$mahnung->versandweg]) : dol_escape_htmltag((string) $mahnung->versandweg))
.' |
';
if (!empty($mahnung->tracking_nr)) {
require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/class/mahnungtrackingpattern.class.php';
$trackUrl = (new MahnungTrackingPattern($db))->urlFor((string) $mahnung->tracking_provider, (string) $mahnung->tracking_nr);
print '| '.$langs->trans('MahnungTrackingNr').' | ';
print ''.dol_escape_htmltag($mahnung->tracking_nr).'';
if (!empty($trackUrl)) {
print ' ';
print img_picto('', 'fa-external-link-alt', 'class="pictofixedwidth"');
print dol_escape_htmltag($langs->trans('MahnungSendungVerfolgen')).'';
}
print ' |
';
}
print '
';
if ($canWrite) {
print '';
}
} elseif ($canWrite) {
// Versand-Formular (Erfassung oder Bearbeitung)
$dateInit = !empty($mahnung->date_versand) ? $mahnung->date_versand : dol_now();
print '';
}
// --- Sendebelege (Beleg-Upload via Dolibarr-Standard) ---
print '
'.$langs->trans('MahnungSendebelege').'
';
print ''.$langs->trans('MahnungSendebelegeHint').'
';
// Tracking-Vorschlaege aus Session-Flash (vom Scan) anzeigen
$suggKey = 'mahnung_tracking_suggestions_'.((int) $mahnung->id);
$suggestions = $_SESSION[$suggKey] ?? null;
if (is_array($suggestions) && !empty($suggestions)) {
print '';
print '
'.$langs->trans('MahnungTrackingErkannt').' ('.count($suggestions).')';
print '
';
foreach ($suggestions as $sg) {
print '';
print '| '.img_picto('', 'pdf', 'class="pictofixedwidth"').dol_escape_htmltag($sg['file']).' | ';
print ''.dol_escape_htmltag($sg['label']).' | ';
print ''.dol_escape_htmltag($sg['nr']).' | ';
print ''.img_picto('', 'fa-external-link-alt').' | ';
print '';
if ($canWrite) {
$applyUrl = $_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id).'&action=apply_tracking'
.'&nr='.urlencode((string) $sg['nr'])
.'&provider='.urlencode((string) $sg['provider'])
.'&token='.newToken();
print ''.dol_escape_htmltag($langs->trans('MahnungTrackingUebernehmen')).'';
}
print ' | ';
print '
';
}
print '
';
if ($canWrite) {
print '
';
}
print '
';
}
// Scan-Button (Belege durchsuchen)
if ($canWrite) {
print '';
}
$mahnungSafeRef = dol_sanitizeFileName($mahnung->ref);
$mahnungFileDir = (!empty($conf->mahnung->multidir_output[$mahnung->entity])
? $conf->mahnung->multidir_output[$mahnung->entity]
: $conf->mahnung->dir_output ?? (DOL_DATA_ROOT.'/mahnung'))
.'/'.$mahnungSafeRef;
// Verzeichnis bei Bedarf anlegen, damit FormFile->showdocuments() das Upload-Formular zeigt
if (!is_dir($mahnungFileDir)) {
dol_mkdir($mahnungFileDir);
}
$urlSelf = $_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id);
$formfile->showdocuments(
'mahnung', // $modulepart
$mahnungSafeRef, // $modulesubdir
$mahnungFileDir, // $filedir
$urlSelf, // $urlsource
0, // $genallowed (kein PDF-Gen-Button hier)
(int) $canWrite, // $delallowed
'', // $modelselected
1, // $allowgenifempty
0, // $forcenomultilang
0, // $iconPDF
0, // $notused
0, // $noform
'', // $param
'', // $title
'', // $buttonlabel
'', // $codelang
'', // $morepicto
$mahnung // $object
);
if ($mahnung->status !== Mahnung::STATUS_STORNIERT && $user->hasRight('mahnung', 'delete')) {
if ($action === 'confirm_storno') {
print $form->formconfirm(
$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id),
$langs->trans('MahnungStornieren'),
$langs->trans('MahnungStornieren').' — '.dol_escape_htmltag($mahnung->ref).'?',
'storno',
'',
0,
1
);
}
}
print '
';
if ($user->hasRight('mahnung', 'write')) {
// Modellauswahl
require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/core/modules/mahnung/modules_mahnung.php';
$modellist = ModelePDFMahnung::liste_modeles($db);
$defaultModel = getDolGlobalString('MAHNUNG_ADDON_PDF', 'standard_mahnung');
if (is_array($modellist) && count($modellist) > 1) {
print '
';
}
print '
';
print $langs->trans('MahnungGenerate');
print ' ';
}
if ($mahnung->status !== Mahnung::STATUS_STORNIERT && $user->hasRight('mahnung', 'delete')) {
print '
';
print $langs->trans('MahnungStornieren');
print '';
}
print '
';
llxFooter();
$db->close();