* * 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/mahnungstufe.class.php * \ingroup mahnung * \brief Konfigurations-Klasse für Mahnstufen (llx_mahnung_stufe). */ require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php'; /** * Eine frei konfigurierbare Mahnstufe: Frist-Konfiguration, Gebühren, optionaler * Zinssatz-Override, Versandart-Default, E-Mail-/PDF-Templates. * * Stufennummern sind frei wählbar (auch 0). Ob eine Stufe die kostenlose * Zahlungserinnerung ist, entscheidet ausschließlich das Flag ist_erinnerung — * NICHT die Stufennummer. */ class MahnungStufe extends CommonObject { /** @var string */ public $element = 'mahnungstufe'; /** @var string */ public $table_element = 'mahnung_stufe'; /** @var int */ public $entity; /** @var int Frei wählbare Stufennummer (0 = Default der Zahlungserinnerung) */ public $stufe; /** @var string */ public $label; /** @var int Wartefrist in Tagen bis diese Stufe fällig wird (ab Fälligkeit bzw. ab Versand der Vorstufe) */ public $frist_tage = 0; /** @var int Neue Zahlungsfrist im Mahnschreiben (Tage) */ public $neue_frist_tage = 7; /** @var float */ public $mahngebuehr_b2c = 0; /** @var float */ public $mahngebuehr_b2b = 0; /** @var int 1 = nur in dieser Stufe Pauschale §288 Abs. 5 berechnen */ public $pauschale_b2b_einmalig = 0; /** @var float|null Override Basiszins+5 % B2C-Default */ public $zinssatz_b2c_uebersteuern; /** @var float|null Override Basiszins+9 % B2B-Default */ public $zinssatz_b2b_uebersteuern; /** @var string pdf|mail|druck|none */ public $versandart_default = 'pdf'; /** @var string */ public $email_subject; /** @var string */ public $email_body; /** @var string */ public $pdf_intro; /** * @var int 0|1 — 1 = kostenlose Zahlungserinnerung: keine Mahngebühr, keine * Pauschale nach §288 Abs. 5, keine Verzugszinsen. Anhang ist die * unveränderte Original-Rechnungs-PDF, Versand nur per E-Mail. */ public $ist_erinnerung = 0; /** @var int 0|1 */ public $active = 1; /** @var int Unix-Zeit */ public $datec; /** @var int Unix-Zeit */ public $tms; /** * @param DoliDB $db Datenbank-Handler */ public function __construct($db) { global $conf; $this->db = $db; $this->entity = $conf->entity; } /** * Stufe anhand ihrer rowid laden (Entity-gefiltert). * * @param int $id rowid * @return int -1 Fehler, 0 nicht gefunden, >0 OK */ public function fetch($id) { $sql = "SELECT t.* FROM ".MAIN_DB_PREFIX."mahnung_stufe as t"; $sql .= " WHERE t.rowid = ".((int) $id); $sql .= " AND t.entity = ".((int) $this->entity); $resql = $this->db->query($sql); if (!$resql) { $this->error = $this->db->lasterror(); return -1; } if (!$this->db->num_rows($resql)) { $this->db->free($resql); return 0; } $obj = $this->db->fetch_object($resql); $this->loadFromObj($obj); $this->db->free($resql); return 1; } /** * @param int $stufe Stufennummer (frei konfigurierbar, 0 möglich) * @return int -1 Fehler, 0 nicht gefunden, >0 OK */ public function fetchByStufe($stufe) { $sql = "SELECT t.* FROM ".MAIN_DB_PREFIX."mahnung_stufe as t"; $sql .= " WHERE t.entity = ".((int) $this->entity); $sql .= " AND t.stufe = ".((int) $stufe); $resql = $this->db->query($sql); if (!$resql) { $this->error = $this->db->lasterror(); return -1; } if (!$this->db->num_rows($resql)) { $this->db->free($resql); return 0; } $obj = $this->db->fetch_object($resql); $this->loadFromObj($obj); $this->db->free($resql); return 1; } /** * Alle aktiven Stufen geordnet nach stufe ASC. * * @return MahnungStufe[] */ public function fetchAllActive() { $sql = "SELECT t.* FROM ".MAIN_DB_PREFIX."mahnung_stufe as t"; $sql .= " WHERE t.entity = ".((int) $this->entity); $sql .= " AND t.active = 1"; $sql .= " ORDER BY t.stufe ASC"; $resql = $this->db->query($sql); $result = array(); if (!$resql) { $this->error = $this->db->lasterror(); return $result; } while ($obj = $this->db->fetch_object($resql)) { $s = new self($this->db); $s->loadFromObj($obj); $result[] = $s; } $this->db->free($resql); return $result; } /** * Neue Mahnstufe anlegen. * * @param User $user Anlegender User * @return int <0 bei Fehler, sonst neue rowid */ public function create($user) { global $conf, $langs; if (!isset($this->stufe) || $this->stufe === '') { $this->error = 'MahnungStufe::create — stufe fehlt'; return -1; } if (empty($this->label)) { $this->error = 'MahnungStufe::create — label fehlt'; return -1; } if (empty($this->entity)) { $this->entity = $conf->entity; } // Stufennummer bereits vergeben? (UNIQUE entity+stufe wuerde sonst mit einer // rohen MySQL-Meldung abbrechen) $check = new self($this->db); $check->entity = $this->entity; $res = $check->fetchByStufe($this->stufe); if ($res < 0) { $this->error = $check->error; return -1; } if ($res > 0) { $this->error = $langs->trans('MahnungStufeNummerVergeben', (int) $this->stufe); return -2; } $now = dol_now(); $this->db->begin(); $sql = "INSERT INTO ".MAIN_DB_PREFIX."mahnung_stufe ("; $sql .= "entity, stufe, label, frist_tage, neue_frist_tage,"; $sql .= " mahngebuehr_b2c, mahngebuehr_b2b, pauschale_b2b_einmalig,"; $sql .= " zinssatz_b2c_uebersteuern, zinssatz_b2b_uebersteuern,"; $sql .= " versandart_default, email_subject, email_body, pdf_intro,"; $sql .= " ist_erinnerung, active, datec"; $sql .= ") VALUES ("; $sql .= ((int) $this->entity).","; $sql .= ((int) $this->stufe).","; $sql .= "'".$this->db->escape($this->label)."',"; $sql .= ((int) $this->frist_tage).","; $sql .= ((int) $this->neue_frist_tage).","; $sql .= ((float) $this->mahngebuehr_b2c).","; $sql .= ((float) $this->mahngebuehr_b2b).","; $sql .= ((int) (!empty($this->pauschale_b2b_einmalig) ? 1 : 0)).","; $sql .= ($this->zinssatz_b2c_uebersteuern !== null && $this->zinssatz_b2c_uebersteuern !== '' ? ((float) $this->zinssatz_b2c_uebersteuern) : "NULL").","; $sql .= ($this->zinssatz_b2b_uebersteuern !== null && $this->zinssatz_b2b_uebersteuern !== '' ? ((float) $this->zinssatz_b2b_uebersteuern) : "NULL").","; $sql .= "'".$this->db->escape($this->versandart_default ?: 'pdf')."',"; $sql .= ($this->email_subject ? "'".$this->db->escape($this->email_subject)."'" : "NULL").","; $sql .= ($this->email_body ? "'".$this->db->escape($this->email_body)."'" : "NULL").","; $sql .= ($this->pdf_intro ? "'".$this->db->escape($this->pdf_intro)."'" : "NULL").","; $sql .= ((int) (!empty($this->ist_erinnerung) ? 1 : 0)).","; $sql .= ((int) (!empty($this->active) ? 1 : 0)).","; $sql .= "'".$this->db->idate($now)."'"; $sql .= ")"; dol_syslog(get_class($this).'::create', LOG_DEBUG); $resql = $this->db->query($sql); if (!$resql) { $this->error = $this->db->lasterror(); $this->db->rollback(); return -1; } $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'mahnung_stufe'); $this->datec = $now; $this->db->commit(); return $this->id; } /** * Mahnstufe löschen. Verweigert das Löschen, solange Mahnvorgänge auf diese * Stufennummer verweisen — sonst würden Bestandsmahnungen ihre Konfiguration * verlieren. In dem Fall ist "deaktivieren" (active = 0) der richtige Weg. * * @param User $user Löschender User * @return int -2 = durch Mahnvorgänge blockiert, <0 = Fehler, sonst 1 */ public function delete($user) { global $langs; if (empty($this->id)) { $this->error = 'MahnungStufe::delete — id missing'; return -1; } // Verweisen bereits Mahnvorgänge auf diese Stufe? $sql = "SELECT COUNT(*) as nb FROM ".MAIN_DB_PREFIX."mahnung_mahnung"; $sql .= " WHERE stufe = ".((int) $this->stufe); $sql .= " AND entity = ".((int) $this->entity); $resql = $this->db->query($sql); if (!$resql) { $this->error = $this->db->lasterror(); return -1; } $obj = $this->db->fetch_object($resql); $this->db->free($resql); if ($obj && (int) $obj->nb > 0) { $this->error = $langs->trans('MahnungStufeNichtLoeschbar'); return -2; } $this->db->begin(); $sql = "DELETE FROM ".MAIN_DB_PREFIX."mahnung_stufe"; $sql .= " WHERE rowid = ".((int) $this->id); $sql .= " AND entity = ".((int) $this->entity); dol_syslog(get_class($this).'::delete', LOG_DEBUG); $resql = $this->db->query($sql); if (!$resql) { $this->error = $this->db->lasterror(); $this->db->rollback(); return -1; } $this->db->commit(); return 1; } /** * Nächste freie Stufennummer der Entity (max(stufe) + 1). * Existiert noch keine Stufe, wird 0 geliefert (Zahlungserinnerung als Einstieg). * * @return int */ public function naechsteFreieStufe() { $sql = "SELECT MAX(stufe) as maxstufe FROM ".MAIN_DB_PREFIX."mahnung_stufe"; $sql .= " WHERE entity = ".((int) $this->entity); $resql = $this->db->query($sql); if (!$resql) { $this->error = $this->db->lasterror(); return 0; } $obj = $this->db->fetch_object($resql); $this->db->free($resql); if (empty($obj) || $obj->maxstufe === null) { return 0; } return ((int) $obj->maxstufe) + 1; } /** * @param User $user * @return int <0 Fehler, sonst rowid */ public function update($user) { if (empty($this->id)) { $this->error = 'MahnungStufe::update — id missing'; return -1; } $sql = "UPDATE ".MAIN_DB_PREFIX."mahnung_stufe SET"; $sql .= " label = '".$this->db->escape($this->label)."'"; $sql .= ", frist_tage = ".((int) $this->frist_tage); $sql .= ", neue_frist_tage = ".((int) $this->neue_frist_tage); $sql .= ", mahngebuehr_b2c = ".((float) $this->mahngebuehr_b2c); $sql .= ", mahngebuehr_b2b = ".((float) $this->mahngebuehr_b2b); $sql .= ", pauschale_b2b_einmalig = ".((int) (!empty($this->pauschale_b2b_einmalig) ? 1 : 0)); $sql .= ", zinssatz_b2c_uebersteuern = ".($this->zinssatz_b2c_uebersteuern !== null && $this->zinssatz_b2c_uebersteuern !== '' ? ((float) $this->zinssatz_b2c_uebersteuern) : "NULL"); $sql .= ", zinssatz_b2b_uebersteuern = ".($this->zinssatz_b2b_uebersteuern !== null && $this->zinssatz_b2b_uebersteuern !== '' ? ((float) $this->zinssatz_b2b_uebersteuern) : "NULL"); $sql .= ", versandart_default = '".$this->db->escape($this->versandart_default ?: 'pdf')."'"; $sql .= ", email_subject = ".($this->email_subject ? "'".$this->db->escape($this->email_subject)."'" : "NULL"); $sql .= ", email_body = ".($this->email_body ? "'".$this->db->escape($this->email_body)."'" : "NULL"); $sql .= ", pdf_intro = ".($this->pdf_intro ? "'".$this->db->escape($this->pdf_intro)."'" : "NULL"); $sql .= ", ist_erinnerung = ".((int) (!empty($this->ist_erinnerung) ? 1 : 0)); $sql .= ", active = ".((int) (!empty($this->active) ? 1 : 0)); $sql .= " WHERE rowid = ".((int) $this->id); // Entity-Filter wie in fetch() und delete(): eine per URL untergeschobene // rowid darf keine Stufen-Konfiguration einer fremden Entity überschreiben. $sql .= " AND entity = ".((int) $this->entity); dol_syslog(get_class($this).'::update', LOG_DEBUG); $resql = $this->db->query($sql); if (!$resql) { $this->error = $this->db->lasterror(); return -1; } return $this->id; } /** * Ist diese Stufe die kostenlose Zahlungserinnerung? * Maßgeblich ist das Flag, nicht die Stufennummer. * * @return bool */ public function istErinnerung() { return !empty($this->ist_erinnerung); } /** * Mahngebühr für einen Kundentyp aus dieser Stufe lesen. * * @param string $kundentyp 'B2C'|'B2B' * @return float */ public function getMahngebuehr($kundentyp) { return $kundentyp === Mahnung::KUNDENTYP_B2B ? (float) $this->mahngebuehr_b2b : (float) $this->mahngebuehr_b2c; } /** * Override-Zinssatz für einen Kundentyp (oder null falls Default gewünscht). * * @param string $kundentyp * @return float|null */ public function getZinssatzOverride($kundentyp) { $value = $kundentyp === Mahnung::KUNDENTYP_B2B ? $this->zinssatz_b2b_uebersteuern : $this->zinssatz_b2c_uebersteuern; return ($value === null || $value === '') ? null : (float) $value; } /** * Properties aus DB-Object-Reihe laden. * * @param object $obj * @return void */ private function loadFromObj($obj) { $this->id = $obj->rowid; $this->entity = $obj->entity; $this->stufe = (int) $obj->stufe; $this->label = $obj->label; $this->frist_tage = (int) $obj->frist_tage; $this->neue_frist_tage = (int) $obj->neue_frist_tage; $this->mahngebuehr_b2c = $obj->mahngebuehr_b2c; $this->mahngebuehr_b2b = $obj->mahngebuehr_b2b; $this->pauschale_b2b_einmalig = (int) $obj->pauschale_b2b_einmalig; $this->zinssatz_b2c_uebersteuern = $obj->zinssatz_b2c_uebersteuern; $this->zinssatz_b2b_uebersteuern = $obj->zinssatz_b2b_uebersteuern; $this->versandart_default = $obj->versandart_default; $this->email_subject = $obj->email_subject; $this->email_body = $obj->email_body; $this->pdf_intro = $obj->pdf_intro; // isset-Guard bleibt bewusst stehen: geladen wird per SELECT t.*, und es gibt // lesende Einstiegspunkte ohne modMahnung::ensureSchema() (Cron, Home-Widget). // Fehlt die Spalte dort, gäbe es sonst eine PHP-Warning "Undefined property". // Fallback 0 = "keine Erinnerung" — die Stufe verhält sich dann wie vor der // Migration, also als normale Mahnstufe. Der Guard betrifft NUR das Lesen; // create()/update() schreiben die Spalte hart und scheitern ohne Migration // sichtbar mit "Unknown column". $this->ist_erinnerung = isset($obj->ist_erinnerung) ? (int) $obj->ist_erinnerung : 0; $this->active = (int) $obj->active; $this->datec = $this->db->jdate($obj->datec); $this->tms = $this->db->jdate($obj->tms); } }