* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 3. */ /** * \file htdocs/custom/mahnung/class/mahnungcron.class.php * \ingroup mahnung * \brief Cron-Job: Vorschlagsliste ueberfaelliger Rechnungen einsammeln, * Ntfy-Push mit Kennzahl an Eddy. */ require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/class/mahnungvorschlag.class.php'; require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/class/mahnungntfy.class.php'; class MahnungCron { /** @var DoliDB */ public $db; /** @var string */ public $error = ''; /** @var string[] */ public $errors = array(); /** @var string */ public $output = ''; /** @var int|string */ public $lastresult = 0; /** * @param DoliDB $db */ public function __construct($db) { $this->db = $db; } /** * Sucht ueberfaellige Rechnungen, ermittelt Vorschlaege je Stufe, * sendet Ntfy-Push mit Anzahl je Stufe und Gesamtwert. * * @return int 0 bei Erfolg, < 0 bei Fehler */ public function buildVorschlagsliste() { global $conf; $service = new MahnungVorschlag($this->db); $vorschlaege = $service->getVorschlaege(); $count = count($vorschlaege); $counts = array(1 => 0, 2 => 0, 3 => 0); $summe = 0.0; foreach ($vorschlaege as $v) { $stufe = (int) $v['vorgeschlagene_stufe']; if (isset($counts[$stufe])) { $counts[$stufe]++; } $summe += (float) $v['betrag_offen']; } $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; } $dolUrl = trim((string) getDolGlobalString('MAIN_INFO_SOCIETE_NOM', '')); $relPath = '/custom/mahnung/list.php?mainmenu=billing&leftmenu=mahnung&mode=vorschlag'; $absUrl = self::buildAbsoluteUrl($relPath); $title = 'Mahnwesen: '.$count.' offene Vorschlaege'; $message = "Stufe 1 (Erinnerung): {$counts[1]}\n"; $message .= "Stufe 2 (Mahnung): {$counts[2]}\n"; $message .= "Stufe 3 (Letzte Mahnung): {$counts[3]}\n"; $message .= 'Offener Betrag: '.number_format($summe, 2, ',', '.').' EUR'; MahnungNtfy::send($title, $message, $absUrl, array('envelope_with_arrow', 'warning')); // Optional: GlobalNotify-Badge ins Dolibarr-UI (wenn Modul aktiv) // Relativer Pfad — wird im Browser-Kontext korrekt aufgeloest if (isModEnabled('globalnotify') && class_exists('GlobalNotify') === false) { $gnPath = DOL_DOCUMENT_ROOT.'/custom/globalnotify/class/globalnotify.class.php'; if (file_exists($gnPath)) { require_once $gnPath; } } if (class_exists('GlobalNotify')) { GlobalNotify::actionRequired( 'mahnung', 'Mahnwesen: '.$count.' Vorschlaege', $message, $relPath, 'Vorschlagsliste oeffnen' ); } $this->output = $title.' — '.$message; $this->lastresult = $count; 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; } $relPath = '/custom/mahnung/list.php?mainmenu=billing&leftmenu=mahnung&mode=archiv'; $absUrl = self::buildAbsoluteUrl($relPath); $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, $absUrl, array('envelope_with_arrow', 'warning')); // Optional: GlobalNotify-Badge — relativer Pfad fuer Browser-Kontext 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, $relPath, '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. * * @param string $relPath * @return string */ private static function buildAbsoluteUrl($relPath) { $base = trim((string) getDolGlobalString('DOLIBARR_MAIN_URL_ROOT', '')); if (empty($base) && defined('DOL_MAIN_URL_ROOT')) { $base = DOL_MAIN_URL_ROOT; } if (empty($base)) { return $relPath; } // Protokoll sicherstellen — ohne Protokoll wird die URL im Browser als relativ interpretiert if (!preg_match('/^https?:\/\//', $base)) { $base = 'http://'.$base; } return rtrim($base, '/').$relPath; } }