Versand-Reminder Cron + Ntfy-Push fuer unversendete Mahnungen [deploy]
Some checks failed
Deploy mahnung / deploy (push) Failing after 4s
Some checks failed
Deploy mahnung / deploy (push) Failing after 4s
Neuer Cron-Job MahnungCronVersandReminder (taeglich):
- Sucht in llx_mahnung_mahnung Status=ERSTELLT (1) + date_versand IS NULL
+ datec < NOW() - INTERVAL N DAY.
- N steht in der Konstante MAHNUNG_VERSAND_REMINDER_DAYS (Default 2).
- Bei Treffern: Ntfy-Push (Topic MAHNUNG_NTFY_TOPIC) mit Titel + Liste
der bis zu 8 Mahnungen ("MAHN2026-0042 (Stufe 2, 3 Tage alt) — Kunde").
- Optional GlobalNotify-Badge "mahnung_versand" wenn GlobalNotify aktiv.
Modul-Descriptor:
- Cronjobs-Array um Reminder ergaenzt (frequency 1d, priority 55, status 1).
Lang-Keys: 2x (de_DE + en_US) fuer Cron-Label + Description.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
216c185fb7
commit
660e91e65d
5 changed files with 133 additions and 0 deletions
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Versand-Reminder (Cron + Ntfy)
|
||||
- Neuer Cron-Job `MahnungCronVersandReminder` (taeglich): sucht Mahnungen mit Status `ERSTELLT` deren PDF seit > N Tagen erstellt aber noch nicht versendet wurde, schickt Ntfy-Push und (falls aktiv) GlobalNotify-Badge.
|
||||
- Schwellenwert konfigurierbar via Konstante `MAHNUNG_VERSAND_REMINDER_DAYS` (Default 2).
|
||||
- Nachricht listet bis zu 8 Mahnungen (Ref + Stufe + Alter in Tagen + Kunde); Rest als "+N weitere".
|
||||
|
||||
### Beleg-Scan mit Sendungsnummer-Erkennung
|
||||
- Neuer Button "Belege scannen" im Versand-Block der Mahnungs-Karte.
|
||||
- Beim Klick werden alle hochgeladenen Belege (PDF via `pdftotext`, sonst txt/html) durchsucht und gegen die konfigurierten Tracking-Patterns gematcht.
|
||||
|
|
|
|||
|
|
@ -66,6 +66,8 @@ class MahnungCron
|
|||
$summe = round($summe, 2);
|
||||
|
||||
if ($count === 0) {
|
||||
// Alte Notifications raeumen — es gibt nichts mehr zu tun
|
||||
self::clearGlobalNotify();
|
||||
$this->output = 'Keine ueberfaelligen Rechnungen mit faelliger Mahnung.';
|
||||
$this->lastresult = 0;
|
||||
return 0;
|
||||
|
|
@ -104,6 +106,115 @@ class MahnungCron
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Raeumt alle GlobalNotify-Notifications fuer das Mahnung-Modul auf.
|
||||
* Wird aufgerufen wenn keine offenen Vorschlaege mehr existieren.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clearGlobalNotify()
|
||||
{
|
||||
if (!isModEnabled('globalnotify')) {
|
||||
return;
|
||||
}
|
||||
if (!class_exists('GlobalNotify')) {
|
||||
$gnPath = DOL_DOCUMENT_ROOT.'/custom/globalnotify/class/globalnotify.class.php';
|
||||
if (!file_exists($gnPath)) {
|
||||
return;
|
||||
}
|
||||
require_once $gnPath;
|
||||
}
|
||||
global $db;
|
||||
$gn = new GlobalNotify($db);
|
||||
$gn->clearModuleNotifications('mahnung');
|
||||
}
|
||||
|
||||
/**
|
||||
* Versand-Reminder: Mahnungen mit Status ERSTELLT, deren PDF schon
|
||||
* laenger als N Tage erstellt wurde, aber noch nicht versendet ist,
|
||||
* werden gesammelt und per Ntfy gepusht.
|
||||
*
|
||||
* Schwellenwert konfigurierbar via MAHNUNG_VERSAND_REMINDER_DAYS (Default 2).
|
||||
*
|
||||
* @return int 0 bei Erfolg, < 0 bei Fehler
|
||||
*/
|
||||
public function versandReminder()
|
||||
{
|
||||
require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/class/mahnung.class.php';
|
||||
|
||||
$tageSchwelle = (int) getDolGlobalString('MAHNUNG_VERSAND_REMINDER_DAYS', '2');
|
||||
if ($tageSchwelle <= 0) {
|
||||
$tageSchwelle = 2;
|
||||
}
|
||||
|
||||
$sql = "SELECT m.rowid, m.ref, m.stufe, m.datec, m.tms, m.fk_facture, m.fk_soc,";
|
||||
$sql .= " s.nom AS soc_nom, f.ref AS facture_ref";
|
||||
$sql .= " FROM ".MAIN_DB_PREFIX."mahnung_mahnung as m";
|
||||
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid = m.fk_soc";
|
||||
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture as f ON f.rowid = m.fk_facture";
|
||||
$sql .= " WHERE m.status = ".Mahnung::STATUS_ERSTELLT;
|
||||
$sql .= " AND m.date_versand IS NULL";
|
||||
$sql .= " AND m.datec < DATE_SUB(NOW(), INTERVAL ".$tageSchwelle." DAY)";
|
||||
$sql .= " ORDER BY m.datec ASC";
|
||||
|
||||
$resql = $this->db->query($sql);
|
||||
if (!$resql) {
|
||||
$this->error = $this->db->lasterror();
|
||||
$this->lastresult = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
$pending = array();
|
||||
while ($obj = $this->db->fetch_object($resql)) {
|
||||
$pending[] = $obj;
|
||||
}
|
||||
$this->db->free($resql);
|
||||
|
||||
if (empty($pending)) {
|
||||
$this->output = 'Keine Mahnungen unversendet > '.$tageSchwelle.' Tage.';
|
||||
$this->lastresult = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
$listUrl = self::buildAbsoluteUrl('/custom/mahnung/list.php?mode=archiv');
|
||||
$title = 'Mahnwesen: '.count($pending).' Mahnung(en) unversendet';
|
||||
$lines = array();
|
||||
foreach ($pending as $p) {
|
||||
$tage = (int) floor((time() - strtotime((string) $p->datec)) / 86400);
|
||||
$lines[] = $p->ref.' (Stufe '.$p->stufe.', '.$tage.' Tage alt) — '.$p->soc_nom;
|
||||
}
|
||||
// Auf 8 Zeilen kuerzen, Rest als "+N weitere"
|
||||
if (count($lines) > 8) {
|
||||
$rest = count($lines) - 8;
|
||||
$lines = array_slice($lines, 0, 8);
|
||||
$lines[] = '+ '.$rest.' weitere';
|
||||
}
|
||||
$message = implode("\n", $lines);
|
||||
|
||||
MahnungNtfy::send($title, $message, $listUrl, array('envelope_with_arrow', 'warning'));
|
||||
|
||||
// Optional: GlobalNotify-Badge
|
||||
if (isModEnabled('globalnotify') && !class_exists('GlobalNotify')) {
|
||||
$gnPath = DOL_DOCUMENT_ROOT.'/custom/globalnotify/class/globalnotify.class.php';
|
||||
if (file_exists($gnPath)) {
|
||||
require_once $gnPath;
|
||||
}
|
||||
}
|
||||
if (class_exists('GlobalNotify')) {
|
||||
GlobalNotify::actionRequired(
|
||||
'mahnung_versand',
|
||||
$title,
|
||||
$message,
|
||||
$listUrl,
|
||||
'Archiv oeffnen'
|
||||
);
|
||||
}
|
||||
|
||||
$this->output = $title.' — '.count($pending).' Eintraege';
|
||||
$this->lastresult = count($pending);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Baut eine absolute URL aus einem relativen Pfad anhand der Dolibarr-URL-Konfig.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -211,6 +211,20 @@ class modMahnung extends DolibarrModules
|
|||
'test' => 'isModEnabled("mahnung")',
|
||||
'priority' => 50,
|
||||
),
|
||||
1 => array(
|
||||
'label' => 'MahnungCronVersandReminder',
|
||||
'jobtype' => 'method',
|
||||
'class' => '/mahnung/class/mahnungcron.class.php',
|
||||
'objectname' => 'MahnungCron',
|
||||
'method' => 'versandReminder',
|
||||
'parameters' => '',
|
||||
'comment' => 'Erinnert per Ntfy an Mahnungen mit Status ERSTELLT, die seit > N Tagen nicht versendet wurden (MAHNUNG_VERSAND_REMINDER_DAYS, Default 2)',
|
||||
'frequency' => 1,
|
||||
'unitfrequency' => 86400,
|
||||
'status' => 1,
|
||||
'test' => 'isModEnabled("mahnung")',
|
||||
'priority' => 55,
|
||||
),
|
||||
);
|
||||
|
||||
// Berechtigungen
|
||||
|
|
|
|||
|
|
@ -177,6 +177,7 @@ MahnungSettingsSaved = Einstellungen gespeichert.
|
|||
# Cron
|
||||
#
|
||||
MahnungCronBuildVorschlag = Mahnwesen — Vorschlagsliste aufbauen
|
||||
MahnungCronVersandReminder = Mahnwesen — Versand-Reminder (unversendete Mahnungen)
|
||||
MahnungCronBuildVorschlagDesc = Sucht taeglich ueberfaellige Rechnungen und sendet einen Ntfy-Push mit der Anzahl neuer Vorschlaege.
|
||||
|
||||
#
|
||||
|
|
|
|||
|
|
@ -178,3 +178,5 @@ MahnungSettingsSaved = Settings saved.
|
|||
#
|
||||
MahnungCronBuildVorschlag = Dunning — build proposal list
|
||||
MahnungCronBuildVorschlagDesc = Daily scan for overdue invoices, sends a Ntfy push with the count of new proposals.
|
||||
MahnungCronVersandReminder = Dunning — shipment reminder (unsent dunnings)
|
||||
MahnungCronVersandReminderDesc = Daily check for dunnings in status ERSTELLT that have not been sent for more than N days (MAHNUNG_VERSAND_REMINDER_DAYS, default 2).
|
||||
|
|
|
|||
Loading…
Reference in a new issue