All checks were successful
Deploy mahnung / deploy (push) Successful in 13s
Wenn pdftotext keinen Text findet, wird ocrmypdf (Tesseract OCR) auf das PDF angewendet bevor erneut nach Sendungsnummern gesucht wird. Bei leerem Ergebnis erscheint jetzt eine Hinweismeldung statt stummem Seiten-Reload. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
681 lines
29 KiB
PHP
681 lines
29 KiB
PHP
<?php
|
|
/* Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
|
|
*
|
|
* 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, $langs->trans('MahnungNichtGefunden', $id));
|
|
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($langs->trans('MahnungDokumentErstellt'), null, 'mesgs');
|
|
} else {
|
|
setEventMessages($langs->trans('MahnungDokumentFehler').': '.$mahnung->error, null, 'errors');
|
|
}
|
|
header('Location: '.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id));
|
|
exit;
|
|
}
|
|
|
|
// Generiertes Dokument löschen
|
|
if ($action === 'delete_doc' && $user->hasRight('mahnung', 'write')) {
|
|
$docfile = GETPOST('file', 'alphanohtml');
|
|
if (!empty($docfile) && !empty($mahnung->fk_facture)) {
|
|
$facTmp = new Facture($db);
|
|
if ($facTmp->fetch((int) $mahnung->fk_facture) > 0) {
|
|
$baseDir = !empty($conf->facture->multidir_output[$facTmp->entity])
|
|
? $conf->facture->multidir_output[$facTmp->entity]
|
|
: $conf->facture->dir_output;
|
|
$fullpath = $baseDir.'/'.dol_sanitizeFileName($facTmp->ref).'/'.dol_sanitizeFileName($docfile);
|
|
// Sicherstellen dass die Datei zur Mahnung gehoert (Ref im Dateinamen)
|
|
if (file_exists($fullpath) && strpos($docfile, dol_sanitizeFileName($mahnung->ref)) !== false) {
|
|
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
|
|
dol_delete_file($fullpath);
|
|
setEventMessages($langs->trans('FileWasRemoved'), null, 'mesgs');
|
|
} else {
|
|
setEventMessages($langs->trans('MahnungDateiNichtGefunden'), 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 ?: $langs->trans('MahnungFehlerSpeichern'), null, 'errors');
|
|
}
|
|
header('Location: '.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id));
|
|
exit;
|
|
}
|
|
|
|
// Rechnung als uneinbringlich klassifizieren (close_code='badcustomer')
|
|
// — endgültiger Schritt nach erfolglosem Mahnverfahren / Vollstreckung.
|
|
if ($action === 'confirm_uneinbringlich' && $user->hasRight('mahnung', 'delete')) {
|
|
require_once DOL_DOCUMENT_ROOT.'/core/class/commoninvoice.class.php';
|
|
$note = trim((string) GETPOST('uneinbringlich_note', 'nohtml'));
|
|
if ($note === '') {
|
|
$note = $langs->trans('MahnungVerfahrenErfolglos').' '.dol_print_date(dol_now(), 'day');
|
|
}
|
|
|
|
$facReload = new Facture($db);
|
|
if ($facReload->fetch((int) $mahnung->fk_facture) > 0) {
|
|
// Rechnung auf STATUS_ABANDONED (3) + close_code='badcustomer' setzen
|
|
$ret = $facReload->setCanceled($user, CommonInvoice::CLOSECODE_BADDEBT, $note);
|
|
if ($ret > 0) {
|
|
// Mahnung mitstornieren — Vorgang ist damit fachlich abgeschlossen
|
|
$mahnung->status = Mahnung::STATUS_STORNIERT;
|
|
$mahnung->note_private = trim(($mahnung->note_private ? $mahnung->note_private."\n\n" : '').$langs->trans('MahnungUneinbringlichKlassifiziert').' ('.dol_print_date(dol_now(), 'day').'): '.$note);
|
|
$mahnung->update($user);
|
|
setEventMessages($langs->trans('MahnungUneinbringlichErfolg'), null, 'mesgs');
|
|
} else {
|
|
setEventMessages($facReload->error ?: $langs->trans('MahnungFehlerKlassifizieren'), null, 'errors');
|
|
}
|
|
} else {
|
|
setEventMessages($langs->trans('MahnungRechnungNichtLadbar'), null, 'errors');
|
|
}
|
|
header('Location: '.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id));
|
|
exit;
|
|
}
|
|
|
|
// Sendungsnummer aus erkanntem Vorschlag übernehmen
|
|
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;
|
|
}
|
|
|
|
// Vorschläge 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';
|
|
|
|
$scanDir = $upload_dir;
|
|
|
|
$patternService = new MahnungTrackingPattern($db);
|
|
$suggestions = array();
|
|
$pdftotextAvailable = null;
|
|
$ocrmypdfAvailable = null;
|
|
|
|
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) {
|
|
// Erst normales pdftotext versuchen
|
|
$cmd = 'pdftotext -layout '.escapeshellarg($file['fullname']).' - 2>/dev/null';
|
|
$text = trim((string) @shell_exec($cmd));
|
|
|
|
// Kein Text? → OCR via ocrmypdf (Bild-PDF lesbar machen)
|
|
if ($text === '') {
|
|
if ($ocrmypdfAvailable === null) {
|
|
$check2 = @shell_exec('command -v ocrmypdf 2>/dev/null');
|
|
$ocrmypdfAvailable = !empty(trim((string) $check2));
|
|
}
|
|
if ($ocrmypdfAvailable) {
|
|
$ocrTmp = $file['fullname'].'.ocr.pdf';
|
|
$ocrCmd = 'ocrmypdf --skip-text -l deu+eng --jobs 2 '
|
|
.escapeshellarg($file['fullname']).' '
|
|
.escapeshellarg($ocrTmp).' 2>/dev/null';
|
|
@shell_exec($ocrCmd);
|
|
if (file_exists($ocrTmp)) {
|
|
$text = trim((string) @shell_exec('pdftotext -layout '.escapeshellarg($ocrTmp).' - 2>/dev/null'));
|
|
@unlink($ocrTmp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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 (empty($suggestions)) {
|
|
setEventMessages($langs->trans('MahnungKeineSendungsnummerErkannt'), null, 'warnings');
|
|
}
|
|
if ($pdftotextAvailable === false) {
|
|
setEventMessages($langs->trans('MahnungPdftotextMissing'), null, 'warnings');
|
|
}
|
|
header('Location: '.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id));
|
|
exit;
|
|
}
|
|
|
|
// Versand-Daten zurücksetzen (z.B. Korrekturmöglichkeit)
|
|
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 zurückdrehen — nur Daten löschen
|
|
if ($mahnung->update($user) > 0) {
|
|
setEventMessages($langs->trans('MahnungVersandGeleert'), null, 'mesgs');
|
|
}
|
|
header('Location: '.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id));
|
|
exit;
|
|
}
|
|
|
|
// Upload-Verzeichnis für Sendebelege (muss VOR llxHeader stehen für actions_linkedfiles)
|
|
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
|
|
$mahnungSafeRef = dol_sanitizeFileName($mahnung->ref);
|
|
$upload_dir = (!empty($conf->mahnung->multidir_output[$mahnung->entity])
|
|
? $conf->mahnung->multidir_output[$mahnung->entity]
|
|
: $conf->mahnung->dir_output ?? (DOL_DATA_ROOT.'/mahnung'))
|
|
.'/'.$mahnungSafeRef;
|
|
if (!is_dir($upload_dir)) {
|
|
dol_mkdir($upload_dir);
|
|
}
|
|
$permissiontoadd = $user->hasRight('mahnung', 'write');
|
|
$permissiontodelete = $user->hasRight('mahnung', 'write');
|
|
include DOL_DOCUMENT_ROOT.'/core/actions_linkedfiles.inc.php';
|
|
|
|
llxHeader('', $langs->trans('MahnungRef').' '.$mahnung->ref);
|
|
|
|
print load_fiche_titre($langs->trans('MahnungRef').' '.$mahnung->ref, '', 'fa-envelope-open-text');
|
|
|
|
print '<div class="fichecenter">';
|
|
print '<div class="underbanner clearboth"></div>';
|
|
print '<table class="border centpercent tableforfield">';
|
|
print '<tr><td class="titlefield fieldname_ref">'.$langs->trans('MahnungRef').'</td><td class="valuefield fieldname_ref">'.dol_escape_htmltag($mahnung->ref).'</td></tr>';
|
|
print '<tr><td>'.$langs->trans('MahnungStufe').'</td><td>'.((int) $mahnung->stufe).'</td></tr>';
|
|
print '<tr><td>'.$langs->trans('MahnungDatum').'</td><td>'.dol_print_date($mahnung->date_mahnung, 'day').'</td></tr>';
|
|
print '<tr><td>'.$langs->trans('MahnungFaelligkeitAlt').'</td><td>'.dol_print_date($mahnung->date_lim_reglement_alt, 'day').'</td></tr>';
|
|
print '<tr><td>'.$langs->trans('MahnungFaelligkeitNeu').'</td><td>'.dol_print_date($mahnung->date_lim_reglement_neu, 'day').'</td></tr>';
|
|
|
|
// Rechnung
|
|
$facture = new Facture($db);
|
|
if ($facture->fetch((int) $mahnung->fk_facture) > 0) {
|
|
print '<tr><td>'.$langs->trans('MahnungRechnung').'</td>';
|
|
print '<td><a href="'.DOL_URL_ROOT.'/compta/facture/card.php?id='.((int) $facture->id).'">'.dol_escape_htmltag($facture->ref).'</a></td></tr>';
|
|
}
|
|
|
|
// Kunde
|
|
$societe = new Societe($db);
|
|
if ($societe->fetch((int) $mahnung->fk_soc) > 0) {
|
|
print '<tr><td>'.$langs->trans('MahnungKunde').'</td>';
|
|
print '<td><a href="'.DOL_URL_ROOT.'/societe/card.php?socid='.((int) $societe->id).'">'.dol_escape_htmltag($societe->name).'</a> ('.dol_escape_htmltag((string) $mahnung->customertype).')</td></tr>';
|
|
}
|
|
|
|
print '<tr><td>'.$langs->trans('MahnungBetragOffen').'</td><td>'.price($mahnung->betrag_offen).'</td></tr>';
|
|
print '<tr><td>'.$langs->trans('MahnungGebuehr').'</td><td>'.price($mahnung->mahngebuehr).'</td></tr>';
|
|
if ((float) $mahnung->pauschale_b2b > 0) {
|
|
print '<tr><td>'.$langs->trans('MahnungPauschaleB2B').'</td><td>'.price($mahnung->pauschale_b2b).'</td></tr>';
|
|
}
|
|
print '<tr><td>'.$langs->trans('MahnungVerzugszinsen').'</td><td>'.price($mahnung->verzugszinsen).' (Basiszins '.number_format((float) $mahnung->basiszins_snapshot, 2, ',', '.').' %)</td></tr>';
|
|
print '<tr><td>'.$langs->trans('MahnungSumme').'</td><td><strong>'.price($mahnung->summe_mahnung).'</strong></td></tr>';
|
|
print '<tr><td>'.$langs->trans('Status').'</td><td>'.dol_escape_htmltag($mahnung->getStatusLabel()).'</td></tr>';
|
|
print '</table>';
|
|
print '</div>'; // Ende fichecenter Stammdaten
|
|
|
|
// --- Generierte Dokumente (wie bei Rechnungen) ---
|
|
print '<br>';
|
|
print load_fiche_titre($langs->trans('Documents'), '', 'fa-file');
|
|
|
|
// Dokumente im Rechnungsordner suchen die zur Mahnung gehoeren
|
|
$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);
|
|
}
|
|
|
|
$fileList = array();
|
|
if (!empty($docDir) && is_dir($docDir)) {
|
|
$allFiles = dol_dir_list($docDir, 'files', 0, preg_quote($mahnungSafeRef, '/'));
|
|
foreach ($allFiles as $f) {
|
|
$fileList[] = $f;
|
|
}
|
|
}
|
|
|
|
if (!empty($fileList)) {
|
|
$canDeleteDoc = $user->hasRight('mahnung', 'write');
|
|
print '<table class="noborder centpercent">';
|
|
print '<tr class="liste_titre">';
|
|
print '<th>'.$langs->trans('Document').'</th>';
|
|
print '<th class="right">'.$langs->trans('Size').'</th>';
|
|
print '<th class="center">'.$langs->trans('Date').'</th>';
|
|
print '<th class="center"></th>';
|
|
print '</tr>';
|
|
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';
|
|
$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 '<tr class="oddeven">';
|
|
// Dateiname mit Icon + Lupe direkt daneben
|
|
print '<td class="nowraponall">';
|
|
print '<a href="'.$dlUrl.'">';
|
|
print img_picto('', $icon, 'class="pictofixedwidth"');
|
|
print dol_escape_htmltag($fname);
|
|
print '</a>';
|
|
if ($ext === 'pdf') {
|
|
print ' <a href="'.$viewUrl.'" target="_blank" title="'.dol_escape_htmltag($langs->trans('Preview')).'">'.img_picto($langs->trans('Preview'), 'search').'</a>';
|
|
}
|
|
print '</td>';
|
|
// Groesse
|
|
print '<td class="right nowraponall">'.dol_print_size($filesize, 0, 0).'</td>';
|
|
// Datum
|
|
print '<td class="center nowraponall">'.dol_print_date($filedate, 'dayhour').'</td>';
|
|
// Aktionen: Download + Löschen
|
|
print '<td class="right nowraponall">';
|
|
print '<a href="'.$dlUrl.'">'.img_picto($langs->trans('Download'), 'download').'</a>';
|
|
if ($canDeleteDoc) {
|
|
$delUrl = $_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id).'&action=delete_doc&file='.urlencode($fname).'&token='.newToken();
|
|
print ' <a href="'.$delUrl.'" onclick="return confirm(\''.dol_escape_js($langs->trans('MahnungDokumentLoeschenConfirm', $fname)).'\');">';
|
|
print img_picto($langs->trans('Delete'), 'delete');
|
|
print '</a>';
|
|
}
|
|
print '</td>';
|
|
print '</tr>';
|
|
}
|
|
print '</table>';
|
|
} else {
|
|
print '<div class="opacitymedium">'.$langs->trans('NoDocuments').'</div>';
|
|
}
|
|
|
|
// 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 '<br>';
|
|
print load_fiche_titre($langs->trans('MahnungVersandBelege'), '', 'fa-truck');
|
|
print '<div class="fichecenter">';
|
|
print '<div class="underbanner clearboth"></div>';
|
|
|
|
// 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 '<table class="border centpercent">';
|
|
print '<tr><td class="titlefield">'.$langs->trans('MahnungVersanddatum').'</td><td>'.dol_print_date($mahnung->date_versand, 'day').'</td></tr>';
|
|
print '<tr><td>'.$langs->trans('MahnungVersandweg').'</td><td>'
|
|
.($mahnung->versandweg && isset($versandwege[$mahnung->versandweg]) ? dol_escape_htmltag($versandwege[$mahnung->versandweg]) : dol_escape_htmltag((string) $mahnung->versandweg))
|
|
.'</td></tr>';
|
|
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 '<tr><td>'.$langs->trans('MahnungTrackingNr').'</td><td>';
|
|
print '<code>'.dol_escape_htmltag($mahnung->tracking_nr).'</code>';
|
|
if (!empty($trackUrl)) {
|
|
print ' <a href="'.dol_escape_htmltag($trackUrl).'" target="_blank" rel="noopener" class="butAction" style="margin-left:8px;">';
|
|
print img_picto('', 'fa-external-link-alt', 'class="pictofixedwidth"');
|
|
print dol_escape_htmltag($langs->trans('MahnungSendungVerfolgen')).'</a>';
|
|
}
|
|
print '</td></tr>';
|
|
}
|
|
print '</table>';
|
|
if ($canWrite) {
|
|
print '<div style="margin-top:8px;">';
|
|
print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id).'&action=edit_versand">'.img_picto('', 'edit').' '.dol_escape_htmltag($langs->trans('MahnungVersandBearbeiten')).'</a> ';
|
|
print '<a class="butActionDelete" href="'.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id).'&action=clear_versand&token='.newToken().'" onclick="return confirm(\''.dol_escape_js($langs->trans('MahnungVersandLeerenConfirm')).'\');">'.dol_escape_htmltag($langs->trans('MahnungVersandLeeren')).'</a>';
|
|
print '</div>';
|
|
}
|
|
} elseif ($canWrite) {
|
|
// Versand-Formular (Erfassung oder Bearbeitung)
|
|
$dateInit = !empty($mahnung->date_versand) ? $mahnung->date_versand : dol_now();
|
|
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id).'">';
|
|
print '<input type="hidden" name="token" value="'.newToken().'">';
|
|
print '<input type="hidden" name="action" value="set_versand">';
|
|
print '<table class="border centpercent">';
|
|
|
|
// Versanddatum
|
|
print '<tr><td class="titlefield">'.$langs->trans('MahnungVersanddatum').'</td><td>';
|
|
print $form->selectDate($dateInit, 'versand_', 0, 0, 0, '', 1, 0);
|
|
print '</td></tr>';
|
|
|
|
// Versandweg
|
|
print '<tr><td>'.$langs->trans('MahnungVersandweg').'</td><td>';
|
|
print '<select name="versandweg" id="versandweg">';
|
|
foreach ($versandwege as $k => $label) {
|
|
print '<option value="'.dol_escape_htmltag($k).'"'.((string) $mahnung->versandweg === $k ? ' selected' : '').'>'.dol_escape_htmltag($label).'</option>';
|
|
}
|
|
print '</select>';
|
|
print '</td></tr>';
|
|
|
|
// Tracking-Nr
|
|
print '<tr><td>'.$langs->trans('MahnungTrackingNr').'</td><td>';
|
|
print '<input type="text" name="tracking_nr" value="'.dol_escape_htmltag((string) $mahnung->tracking_nr).'" size="30" placeholder="'.dol_escape_htmltag($langs->trans('MahnungTrackingNrHint')).'">';
|
|
print ' <span class="opacitymedium">('.dol_escape_htmltag($langs->trans('MahnungTrackingProviderAuto')).')</span>';
|
|
print '</td></tr>';
|
|
|
|
// Optional: Tracking-Provider override
|
|
print '<tr><td>'.$langs->trans('MahnungTrackingProvider').'</td><td>';
|
|
print '<select name="tracking_provider">';
|
|
print '<option value="">'.dol_escape_htmltag($langs->trans('MahnungTrackingProviderAuto')).'</option>';
|
|
foreach (array('dhl' => 'DHL', 'dpag' => 'Deutsche Post', 'dpd' => 'DPD', 'hermes' => 'Hermes', 'ups' => 'UPS') as $k => $label) {
|
|
print '<option value="'.dol_escape_htmltag($k).'"'.((string) $mahnung->tracking_provider === $k ? ' selected' : '').'>'.$label.'</option>';
|
|
}
|
|
print '</select>';
|
|
print '</td></tr>';
|
|
|
|
print '</table>';
|
|
print '<div style="margin-top:8px;">';
|
|
print '<button type="submit" class="button">'.dol_escape_htmltag($langs->trans('Save')).'</button> ';
|
|
if (!empty($mahnung->date_versand)) {
|
|
print '<a class="butActionRefused" href="'.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id).'">'.dol_escape_htmltag($langs->trans('Cancel')).'</a>';
|
|
}
|
|
print '</div>';
|
|
print '</form>';
|
|
}
|
|
|
|
// --- Sendebelege (Beleg-Upload via Dolibarr-Standard) ---
|
|
print '<br>';
|
|
print load_fiche_titre($langs->trans('MahnungSendebelege'), '', 'fa-paperclip');
|
|
print '<div class="opacitymedium" style="margin-bottom:8px;">'.$langs->trans('MahnungSendebelegeHint').'</div>';
|
|
|
|
// Tracking-Vorschläge aus Session-Flash (vom Scan) anzeigen
|
|
$suggKey = 'mahnung_tracking_suggestions_'.((int) $mahnung->id);
|
|
$suggestions = $_SESSION[$suggKey] ?? null;
|
|
if (is_array($suggestions) && !empty($suggestions)) {
|
|
print '<div class="info" style="padding:8px; margin-bottom:8px; border-left:3px solid #2a8;">';
|
|
print '<strong>'.$langs->trans('MahnungTrackingErkannt').'</strong> <span class="opacitymedium">('.count($suggestions).')</span>';
|
|
print '<table class="noborder" style="margin-top:6px;">';
|
|
foreach ($suggestions as $sg) {
|
|
print '<tr>';
|
|
print '<td class="nowrap">'.img_picto('', 'pdf', 'class="pictofixedwidth"').dol_escape_htmltag($sg['file']).'</td>';
|
|
print '<td>'.dol_escape_htmltag($sg['label']).'</td>';
|
|
print '<td><code><strong>'.dol_escape_htmltag($sg['nr']).'</strong></code></td>';
|
|
print '<td><a href="'.dol_escape_htmltag($sg['url']).'" target="_blank" rel="noopener" class="opacitymedium">'.img_picto('', 'fa-external-link-alt').'</a></td>';
|
|
print '<td>';
|
|
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 '<a class="button smallpaddingimp" href="'.$applyUrl.'">'.dol_escape_htmltag($langs->trans('MahnungTrackingUebernehmen')).'</a>';
|
|
}
|
|
print '</td>';
|
|
print '</tr>';
|
|
}
|
|
print '</table>';
|
|
if ($canWrite) {
|
|
print '<div style="margin-top:6px;"><a class="opacitymedium" href="'.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id).'&action=dismiss_tracking&token='.newToken().'">'.dol_escape_htmltag($langs->trans('MahnungTrackingVerwerfen')).'</a></div>';
|
|
}
|
|
print '</div>';
|
|
}
|
|
|
|
// Scan-Button (Belege durchsuchen)
|
|
if ($canWrite) {
|
|
print '<div style="margin-bottom:8px;">';
|
|
print '<a class="button smallpaddingimp" href="'.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id).'&action=scan_belege&token='.newToken().'">';
|
|
print img_picto('', 'fa-search', 'class="pictofixedwidth"').dol_escape_htmltag($langs->trans('MahnungBelegeScannen'));
|
|
print '</a> ';
|
|
print '<span class="opacitymedium small">'.$langs->trans('MahnungBelegeScannenHint').'</span>';
|
|
print '</div>';
|
|
}
|
|
|
|
$urlSelf = $_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id);
|
|
|
|
// Upload-Formular (Durchsuchen + Upload-Button)
|
|
$formfile->form_attach_new_file(
|
|
$urlSelf,
|
|
'', // title
|
|
0, // addcancel
|
|
0, // sectionid
|
|
(int) $canWrite, // perm
|
|
50, // size
|
|
$mahnung, // object
|
|
'', // options
|
|
1, // useajax
|
|
'', // savingdocmask
|
|
0, // linkfiles
|
|
'formuserfile', // htmlname
|
|
'', // accept
|
|
'', // sectiondir
|
|
0, // usewithoutform
|
|
0, // capture
|
|
0 // disablemulti
|
|
);
|
|
|
|
// Dateiliste der bereits hochgeladenen Belege
|
|
print $formfile->showdocuments(
|
|
'mahnung', // $modulepart
|
|
$mahnungSafeRef, // $modulesubdir
|
|
$upload_dir, // $filedir
|
|
$urlSelf, // $urlsource
|
|
0, // $genallowed (kein PDF-Gen-Button hier)
|
|
(int) $canWrite, // $delallowed
|
|
'', // $modelselected
|
|
0, // $allowgenifempty
|
|
0, // $forcenomultilang
|
|
0, // $iconPDF
|
|
0, // $notused
|
|
0, // $noform
|
|
'', // $param
|
|
'', // $title
|
|
'', // $buttonlabel
|
|
'', // $codelang
|
|
'', // $morepicto
|
|
$mahnung, // $object
|
|
0 // $hideifempty
|
|
);
|
|
|
|
print '</div>'; // Ende fichecenter Versand & Belege
|
|
|
|
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
|
|
);
|
|
}
|
|
if ($action === 'ask_uneinbringlich') {
|
|
// Bestätigungsdialog mit Eingabefeld für Begründung
|
|
$formquestion = array(
|
|
array(
|
|
'type' => 'textarea',
|
|
'name' => 'uneinbringlich_note',
|
|
'label' => $langs->trans('MahnungUneinbringlichBegruendung'),
|
|
'value' => $langs->trans('MahnungVerfahrenErfolglos').' '.dol_print_date(dol_now(), 'day'),
|
|
'morecss' => 'minwidth500 quatrevingtpercent',
|
|
),
|
|
);
|
|
print $form->formconfirm(
|
|
$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id),
|
|
$langs->trans('MahnungUneinbringlichTitel'),
|
|
$langs->trans('MahnungUneinbringlichWarnung').' '.dol_escape_htmltag($mahnung->ref).'?',
|
|
'confirm_uneinbringlich',
|
|
$formquestion,
|
|
0,
|
|
1,
|
|
300
|
|
);
|
|
}
|
|
}
|
|
|
|
print '<br><div class="tabsAction">';
|
|
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 '<select name="model" id="selectmodel" style="margin-right: 5px;">';
|
|
foreach ($modellist as $k => $v) {
|
|
print '<option value="'.dol_escape_htmltag($k).'"'.($k === $defaultModel ? ' selected' : '').'>'.dol_escape_htmltag($v).'</option>';
|
|
}
|
|
print '</select>';
|
|
}
|
|
|
|
print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id).'&action=regenerate_pdf&token='.newToken().'" onclick="if(document.getElementById(\'selectmodel\')){this.href+=\'&model=\'+document.getElementById(\'selectmodel\').value;}">';
|
|
print $langs->trans('MahnungGenerate');
|
|
print '</a> ';
|
|
}
|
|
// "Als uneinbringlich klassifizieren" — nur für Stufe-3-Mahnungen mit
|
|
// Status >= ERSTELLT (also nicht im Entwurf) und nicht bereits storniert.
|
|
// Setzt die Rechnung auf STATUS_ABANDONED mit close_code='badcustomer'.
|
|
if ($mahnung->status !== Mahnung::STATUS_STORNIERT
|
|
&& (int) $mahnung->stufe === 3
|
|
&& (int) $mahnung->status >= Mahnung::STATUS_ERSTELLT
|
|
&& $user->hasRight('mahnung', 'delete')
|
|
) {
|
|
// Prüfen ob Rechnung schon abandoned ist — dann Button verstecken
|
|
$facStatus = 0;
|
|
$facRes = $db->query("SELECT fk_statut, close_code FROM ".MAIN_DB_PREFIX."facture WHERE rowid = ".((int) $mahnung->fk_facture));
|
|
if ($facRes && ($facObj = $db->fetch_object($facRes))) {
|
|
$facStatus = (int) $facObj->fk_statut;
|
|
$db->free($facRes);
|
|
}
|
|
if ($facStatus !== 3) {
|
|
print '<a class="butActionDelete" href="'.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id).'&action=ask_uneinbringlich" title="'.dol_escape_htmltag($langs->trans('MahnungUneinbringlichHint')).'">';
|
|
print img_picto('', 'warning', 'class="pictofixedwidth"');
|
|
print $langs->trans('MahnungUneinbringlich');
|
|
print '</a>';
|
|
} else {
|
|
// Rechnung ist schon als abandoned markiert — Info anzeigen
|
|
print '<span class="opacitymedium" style="margin-left:8px;">'.dol_escape_htmltag($langs->trans('MahnungRechnungBereitsUneinbringlich')).'</span>';
|
|
}
|
|
}
|
|
if ($mahnung->status !== Mahnung::STATUS_STORNIERT && $user->hasRight('mahnung', 'delete')) {
|
|
print '<a class="butActionDelete" href="'.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id).'&action=confirm_storno">';
|
|
print $langs->trans('MahnungStornieren');
|
|
print '</a>';
|
|
}
|
|
print '</div>';
|
|
|
|
llxFooter();
|
|
$db->close();
|