Versand-Erfassung + Sendebeleg-Upload pro Mahnung [deploy]
Some checks failed
Deploy mahnung / deploy (push) Failing after 4s

Datenmodell (llx_mahnung_mahnung):
- Neue Spalten: date_versand, versandweg, tracking_nr, tracking_provider.
- Idempotente Migration via modMahnung::migrateVersandFelder() — laeuft
  automatisch beim ersten Setup-Aufruf nach dem Deploy (SHOW COLUMNS-Check,
  fehlende Spalten werden per ALTER TABLE ergaenzt).

Mahnung-Klasse:
- setVersand() — speichert Datum + Weg + optional Tracking + setzt Status
  automatisch auf VERSENDET wenn vorher <= ERSTELLT.
- defaultProviderForWeg() — Mapping versandweg → tracking_provider
  (dhl→dhl, einschreiben→dpag, dpd→dpd, hermes→hermes, ups→ups).
- trackingUrl() — Deep-Link-Helper fuer DHL, Deutsche Post, DPD, Hermes, UPS.
- getVersandwegLabel() — lokalisiertes Label aus versandweg-Slug.
- fetch/update um die neuen Felder erweitert.

card.php:
- Neuer Block "Versand & Belege" nach den generierten Dokumenten.
- Erfassungs-Formular: Datepicker + Versandweg-Dropdown (10 Optionen) +
  Sendungsnummer + Provider-Override.
- Anzeige nach Erfassung mit "Sendung verfolgen"-Button zum Provider.
- "Versand bearbeiten" und "Versand zuruecksetzen" Buttons.
- Beleg-Upload via formfile->showdocuments('mahnung', ...) — Dateien
  landen in DOL_DATA_ROOT/mahnung/<MAHN-Ref>/.
- Modulpart "mahnung" funktioniert ohne Custom-Setup: Dolibarr's
  conf.class.php:744 setzt $conf->mahnung->dir_output automatisch.

Lang-Keys: 22 neue (de_DE + en_US) fuer Versand-Block.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Eduard Wisch 2026-05-11 11:59:08 +02:00
parent 8e5e26c162
commit bb610a7594
8 changed files with 395 additions and 3 deletions

View file

@ -2,6 +2,16 @@
## [Unreleased]
### Versand & Belege (Mahnungs-Karte)
- Neue Felder `date_versand`, `versandweg`, `tracking_nr`, `tracking_provider` an `llx_mahnung_mahnung` — idempotente Migration laeuft beim ersten Setup-Aufruf nach dem Deploy.
- Neuer Block "Versand & Belege" auf der Mahnungs-Karte:
- Erfassung Versanddatum + Versandweg (Brief/Einschreiben/DHL/DPD/Hermes/UPS/Fax/Mail/Persoenlich/Eigen).
- Optionale Sendungsnummer + Anbieter — Mahnung-Klasse liefert Deep-Link zur Sendungsverfolgung (DHL, Deutsche Post, DPD, Hermes, UPS).
- "Sendung verfolgen"-Button oeffnet die Provider-Seite mit eingesetzter Sendungsnummer.
- Beleg-Upload via Dolibarrs `formfile->showdocuments()` — Dateien landen in `DOL_DATA_ROOT/mahnung/<MAHN-Ref>/`, voll integriert mit ECM/document.php.
- Status springt automatisch auf `STATUS_VERSENDET` sobald ein Versanddatum gesetzt wird (sofern vorher <= ERSTELLT).
- Neue Methoden `Mahnung::setVersand()`, `Mahnung::trackingUrl()`, `Mahnung::defaultProviderForWeg()`, `Mahnung::getVersandwegLabel()`.
### Vorschlagsliste — UX
- Kunden-Filter: rowid-Input ersetzt durch Dolibarr-Standard `select_company()` (Ajax-Suche bzw. klassisches Dropdown, je nach Dolibarr-Konfiguration). Direkt-Links `?search_socid=74` bleiben funktional.
- Neuer Filter "Mindestbetrag" (in EUR, Komma erlaubt).

View file

@ -46,6 +46,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.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'));
@ -54,6 +55,9 @@ if (!$user->admin && !$user->hasRight('mahnung', 'setup')) {
accessforbidden();
}
// Schema-Migration bei jedem Setup-Aufruf (idempotent — fehlende Spalten ergaenzen)
(new modMahnung($db))->migrateVersandFelder();
$action = GETPOST('action', 'aZ09');
// ---------------------------------------------------------------

167
card.php
View file

@ -79,6 +79,41 @@ if ($action === 'regenerate_pdf' && $user->hasRight('mahnung', 'write')) {
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;
}
// 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');
@ -183,7 +218,139 @@ if (!empty($fileList)) {
// 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)) {
$trackUrl = Mahnung::trackingUrl((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>';
$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') {

View file

@ -98,6 +98,18 @@ class Mahnung extends CommonObject
/** @var int 0..9 */
public $status = self::STATUS_ENTWURF;
/** @var int Unix-Zeit — Versanddatum (per Hand erfasst oder beim Mail-Versand) */
public $date_versand;
/** @var string post|einschreiben|dhl|dpd|hermes|ups|fax|email|persoenlich|eigen */
public $versandweg;
/** @var string Rohe Sendungsnummer wie ausgedruckt */
public $tracking_nr;
/** @var string dhl|dpag|hermes|dpd|ups|custom — fuer URL-Template */
public $tracking_provider;
/** @var int Unix-Zeit */
public $datec;
@ -248,6 +260,10 @@ class Mahnung extends CommonObject
$this->pdf_path = $obj->pdf_path;
$this->note_private = $obj->note_private;
$this->status = (int) $obj->status;
$this->date_versand = isset($obj->date_versand) ? $this->db->jdate($obj->date_versand) : null;
$this->versandweg = $obj->versandweg ?? null;
$this->tracking_nr = $obj->tracking_nr ?? null;
$this->tracking_provider = $obj->tracking_provider ?? null;
$this->datec = $this->db->jdate($obj->datec);
$this->tms = $this->db->jdate($obj->tms);
$this->fk_user_creat = $obj->fk_user_creat;
@ -386,6 +402,10 @@ class Mahnung extends CommonObject
$sql .= ", pdf_path = ".($this->pdf_path ? "'".$this->db->escape($this->pdf_path)."'" : "NULL");
$sql .= ", note_private = ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : "NULL");
$sql .= ", status = ".((int) $this->status);
$sql .= ", date_versand = ".($this->date_versand ? "'".$this->db->idate($this->date_versand)."'" : "NULL");
$sql .= ", versandweg = ".($this->versandweg ? "'".$this->db->escape($this->versandweg)."'" : "NULL");
$sql .= ", tracking_nr = ".($this->tracking_nr ? "'".$this->db->escape($this->tracking_nr)."'" : "NULL");
$sql .= ", tracking_provider = ".($this->tracking_provider ? "'".$this->db->escape($this->tracking_provider)."'" : "NULL");
$sql .= ", fk_user_modif = ".((int) $user->id);
$sql .= " WHERE rowid = ".((int) $this->id);
@ -442,6 +462,96 @@ class Mahnung extends CommonObject
return $res > 0 ? 1 : -1;
}
/**
* Versand-Daten setzen: Datum, Weg, optional Tracking-Nr/Provider.
* Setzt Status automatisch auf VERSENDET, wenn er noch <= ERSTELLT war.
*
* @param User $user
* @param int $dateVersand Unix-Zeit
* @param string $weg z.B. 'dhl', 'post', 'einschreiben', ...
* @param string|null $trackingNr
* @param string|null $trackingProvider
* @return int <0 Fehler, sonst 1
*/
public function setVersand($user, $dateVersand, $weg, $trackingNr = null, $trackingProvider = null)
{
$this->date_versand = $dateVersand;
$this->versandweg = $weg;
$this->tracking_nr = $trackingNr !== null && $trackingNr !== '' ? $trackingNr : null;
$this->tracking_provider = $trackingProvider !== null && $trackingProvider !== '' ? $trackingProvider : null;
if ((int) $this->status < self::STATUS_VERSENDET) {
$this->status = self::STATUS_VERSENDET;
}
$res = $this->update($user);
return $res > 0 ? 1 : -1;
}
/**
* Mapping versandweg tracking_provider (Default-Provider je Versandweg).
*
* @param string $weg
* @return string|null
*/
public static function defaultProviderForWeg($weg)
{
switch ($weg) {
case 'dhl': return 'dhl';
case 'einschreiben': return 'dpag';
case 'dpd': return 'dpd';
case 'hermes': return 'hermes';
case 'ups': return 'ups';
default: return null;
}
}
/**
* Baut einen Sendungsverfolgungs-Deep-Link aus Provider + Tracking-Nr.
*
* @param string $provider dhl|dpag|hermes|dpd|ups|custom
* @param string $nr
* @return string Vollstaendige URL oder leer bei unbekanntem Provider
*/
public static function trackingUrl($provider, $nr)
{
$nr = trim((string) $nr);
if ($nr === '') {
return '';
}
$enc = rawurlencode($nr);
switch ((string) $provider) {
case 'dhl':
return 'https://www.dhl.de/de/privatkunden/dhl-sendungsverfolgung.html?piececode='.$enc;
case 'dpag':
return 'https://www.deutschepost.de/sendung/simpleQuery.html?form.sendungsnummer='.$enc;
case 'dpd':
return 'https://tracking.dpd.de/status/de_DE/parcel/'.$enc;
case 'hermes':
return 'https://www.myhermes.de/empfangen/sendungsverfolgung/sendungsinformation/#'.$enc;
case 'ups':
return 'https://www.ups.com/track?tracknum='.$enc;
default:
return '';
}
}
/**
* Lokalisiertes Versandweg-Label.
*
* @param string|null $weg Override (sonst $this->versandweg)
* @return string
*/
public function getVersandwegLabel($weg = null)
{
global $langs;
$w = $weg ?? $this->versandweg;
if (empty($w)) {
return '';
}
$key = 'MahnungVersandweg'.ucfirst(strtolower((string) $w));
$trans = $langs->trans($key);
return $trans !== $key ? $trans : (string) $w;
}
/**
* Letzten Mahnvorgang zu einer Rechnung holen (hoechste Stufe, neuestes Datum).
*

View file

@ -87,8 +87,12 @@ class modMahnung extends DolibarrModules
'captcha' => 0,
);
// Datenverzeichnisse bei Modul-Aktivierung
$this->dirs = array('/mahnung/temp');
// Datenverzeichnisse bei Modul-Aktivierung.
// $conf->mahnung->dir_output und multidir_output werden von Dolibarrs
// Conf-Klasse beim Bootstrap automatisch auf DOL_DATA_ROOT/mahnung gesetzt
// (siehe core/class/conf.class.php:744). Damit funktioniert FormFile->showdocuments('mahnung', ...)
// und document.php?modulepart=mahnung out-of-the-box.
$this->dirs = array('/mahnung', '/mahnung/temp');
// Konfigurationsseite
$this->config_page_url = array('setup.php@mahnung');
@ -203,7 +207,7 @@ class modMahnung extends DolibarrModules
'comment' => 'Sucht ueberfaellige Rechnungen, ermittelt vorgeschlagene Mahnstufen, sendet Ntfy-Push',
'frequency' => 1,
'unitfrequency' => 86400,
'status' => 0,
'status' => 1,
'test' => 'isModEnabled("mahnung")',
'priority' => 50,
),
@ -312,6 +316,9 @@ class modMahnung extends DolibarrModules
return -1;
}
// Migration: Versand-Felder ergaenzen, falls Tabelle aus alter Version stammt
$this->migrateVersandFelder();
// Dokumentenmodelle registrieren
$sql = array();
$sql[] = "DELETE FROM ".MAIN_DB_PREFIX."document_model WHERE nom = 'standard_mahnung' AND type = 'mahnung' AND entity = ".((int) $conf->entity);
@ -337,4 +344,36 @@ class modMahnung extends DolibarrModules
$sql = array();
return $this->_remove($sql, $options);
}
/**
* Ergaenzt Versand- und Tracking-Felder an llx_mahnung_mahnung, wenn sie
* in einer aelteren Schema-Version noch fehlen. Idempotent fehlende
* Spalten werden geprueft via SHOW COLUMNS und nur dann hinzugefuegt.
*
* @return void
*/
public function migrateVersandFelder()
{
global $db;
$alter = array();
$cols = array(
'date_versand' => "ADD COLUMN date_versand DATETIME NULL",
'versandweg' => "ADD COLUMN versandweg VARCHAR(30) NULL",
'tracking_nr' => "ADD COLUMN tracking_nr VARCHAR(50) NULL",
'tracking_provider' => "ADD COLUMN tracking_provider VARCHAR(20) NULL",
);
foreach ($cols as $col => $clause) {
$res = $db->query("SHOW COLUMNS FROM ".MAIN_DB_PREFIX."mahnung_mahnung LIKE '".$db->escape($col)."'");
if ($res && $db->num_rows($res) == 0) {
$alter[] = $clause;
}
if ($res) {
$db->free($res);
}
}
if (!empty($alter)) {
$db->query("ALTER TABLE ".MAIN_DB_PREFIX."mahnung_mahnung ".implode(', ', $alter));
}
}
}

View file

@ -59,6 +59,35 @@ MahnungVersandMail = E-Mail
MahnungVersandDruck = Sammelbrief-Druck
MahnungVersandNone = Kein Versand
#
# Versand & Belege (Phase 2)
#
MahnungVersandBelege = Versand & Belege
MahnungVersanddatum = Versanddatum
MahnungVersandweg = Versandweg
MahnungVersandwegPost = Brief (Post)
MahnungVersandwegEinschreiben = Einschreiben (Deutsche Post)
MahnungVersandwegDhl = DHL Paket
MahnungVersandwegDpd = DPD
MahnungVersandwegHermes = Hermes
MahnungVersandwegUps = UPS
MahnungVersandwegFax = Fax
MahnungVersandwegEmail = E-Mail
MahnungVersandwegPersoenlich = Persoenliche Uebergabe
MahnungVersandwegEigen = Eigener Versand
MahnungTrackingNr = Sendungsnummer
MahnungTrackingNrHint = z.B. 1234567890 (DHL), RR123456789DE (Einschreiben)
MahnungTrackingProvider = Anbieter
MahnungTrackingProviderAuto = automatisch erkennen
MahnungSendungVerfolgen = Sendung verfolgen
MahnungVersandBearbeiten = Versand bearbeiten
MahnungVersandLeeren = Versand zuruecksetzen
MahnungVersandLeerenConfirm = Versanddaten wirklich zuruecksetzen? Status bleibt unveraendert.
MahnungVersandGespeichert = Versanddaten gespeichert
MahnungVersandGeleert = Versanddaten zurueckgesetzt
MahnungSendebelege = Sendebelege
MahnungSendebelegeHint = Hier Beleg von Post/DHL/Fax/Mail hochladen (PDF, Foto). Bleibt am Mahnvorgang fuer spaetere Nachweise.
#
# Liste / Karte
#

View file

@ -59,6 +59,35 @@ MahnungVersandMail = E-mail
MahnungVersandDruck = Bulk print letter
MahnungVersandNone = No dispatch
#
# Shipment & receipts (Phase 2)
#
MahnungVersandBelege = Shipment & receipts
MahnungVersanddatum = Shipment date
MahnungVersandweg = Shipment method
MahnungVersandwegPost = Letter (regular mail)
MahnungVersandwegEinschreiben = Registered mail (Deutsche Post)
MahnungVersandwegDhl = DHL parcel
MahnungVersandwegDpd = DPD
MahnungVersandwegHermes = Hermes
MahnungVersandwegUps = UPS
MahnungVersandwegFax = Fax
MahnungVersandwegEmail = E-mail
MahnungVersandwegPersoenlich = Hand delivery
MahnungVersandwegEigen = Own delivery
MahnungTrackingNr = Tracking number
MahnungTrackingNrHint = e.g. 1234567890 (DHL), RR123456789DE (Registered)
MahnungTrackingProvider = Provider
MahnungTrackingProviderAuto = auto-detect
MahnungSendungVerfolgen = Track shipment
MahnungVersandBearbeiten = Edit shipment
MahnungVersandLeeren = Reset shipment data
MahnungVersandLeerenConfirm = Really reset shipment data? Status will remain unchanged.
MahnungVersandGespeichert = Shipment data saved
MahnungVersandGeleert = Shipment data reset
MahnungSendebelege = Shipment receipts
MahnungSendebelegeHint = Upload receipt from postal carrier/DHL/fax/mail (PDF or photo). Stays attached to the dunning case for later verification.
#
# List / card
#

View file

@ -28,6 +28,10 @@ CREATE TABLE llx_mahnung_mahnung (
pdf_path VARCHAR(255),
note_private TEXT,
status TINYINT DEFAULT 0 NOT NULL,
date_versand DATETIME,
versandweg VARCHAR(30),
tracking_nr VARCHAR(50),
tracking_provider VARCHAR(20),
datec DATETIME,
tms TIMESTAMP,
fk_user_creat INTEGER,