All checks were successful
Deploy mahnung / deploy (push) Successful in 14s
Umlaute in allen lang-Dateien korrigiert. Alle hardcodierten deutschen Strings
in 22 PHP-Dateien durch $langs->trans('Key') ersetzt. Neue Schlüssel für
Cron-Meldungen, Dokument-Aktionen, Bonität, Vorschlag-Status, Template-Vars u.a.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
649 lines
28 KiB
PHP
649 lines
28 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; // einmalig prüfen
|
|
|
|
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 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 '<table class="border centpercent">';
|
|
print '<tr><td class="titlefield">'.$langs->trans('MahnungRef').'</td><td>'.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>';
|
|
|
|
// --- 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');
|
|
|
|
// 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><h3>'.$langs->trans('MahnungSendebelege').'</h3>';
|
|
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
|
|
$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
|
|
1 // $hideifempty (Liste nur zeigen wenn Dateien vorhanden)
|
|
);
|
|
|
|
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();
|