From 993ac50c0c36a03044b0780f180ddd14dbf2c49a Mon Sep 17 00:00:00 2001 From: Eduard Wisch Date: Sun, 10 May 2026 18:28:24 +0200 Subject: [PATCH] Feature: ODT-Template-System, Widget, Dokumentenliste, Templatevariablen-Referenz [deploy] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ODT-Template-System: Pro Mahnstufe eigenes Template (mahnung_stufe1/2/3.odt), Fallback auf generisches Template; Basis-Klasse ModelePDFMahnung, pdf_standard_mahnung (TCPDF refactored), doc_generic_mahnung_odt (ODTPHP) - Widget box_mahnung_offen: Überfällige Rechnungen mit Mahnstufe-Badge (blau/orange/rot) - card.php: Dokumentenliste mit Lupe (PDF-Vorschau), Download, Modellauswahl - admin/templatevars.php: Referenzseite aller verfügbaren ODT-Variablen - admin/setup.php: Dokumentenmodell-Verwaltung, Upload-Bereich mit Benennungskonvention - mahnung.class.php: generateDocument() + socid-Alias für commonGenerateDocument() - modMahnung.class.php: models=1, MAHNUNG_ADDON_PDF/ODT_PATH-Konstanten, document_model-Registrierung in init(), Widget registriert - mahnungpdf.class.php entfernt, Logik in pdf_standard_mahnung.modules.php Co-Authored-By: Claude Opus 4.6 (1M context) --- admin/setup.php | 117 ++++ admin/templatevars.php | 220 ++++++++ ajax/createmahnung.php | 8 +- card.php | 134 ++++- class/mahnung.class.php | 37 ++ core/boxes/box_mahnung_offen.php | 254 +++++++++ .../doc/doc_generic_mahnung_odt.modules.php | 504 ++++++++++++++++++ .../doc/pdf_standard_mahnung.modules.php | 171 +++--- core/modules/mahnung/modules_mahnung.php | 55 ++ core/modules/modMahnung.class.php | 39 +- langs/de_DE/mahnung.lang | 13 + 11 files changed, 1432 insertions(+), 120 deletions(-) create mode 100644 admin/templatevars.php create mode 100644 core/boxes/box_mahnung_offen.php create mode 100644 core/modules/mahnung/doc/doc_generic_mahnung_odt.modules.php rename class/mahnungpdf.class.php => core/modules/mahnung/doc/pdf_standard_mahnung.modules.php (67%) create mode 100644 core/modules/mahnung/modules_mahnung.php diff --git a/admin/setup.php b/admin/setup.php index 5cb9691..b8a2516 100644 --- a/admin/setup.php +++ b/admin/setup.php @@ -42,8 +42,10 @@ if (!$res) { } require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; +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'; global $langs, $user, $conf, $db; $langs->loadLangs(array('admin', 'mahnung@mahnung')); @@ -165,6 +167,35 @@ function loadStufeById($db, $id, $entity) return $s; } +// --------------------------------------------------------------- +// Dokumentenmodell-Aktionen (vor View, da header()-Redirect) +// --------------------------------------------------------------- +if ($action === 'setdoc' && $user->hasRight('mahnung', 'setup')) { + $newModel = GETPOST('value', 'alphanohtml'); + if (!empty($newModel)) { + dolibarr_set_const($db, 'MAHNUNG_ADDON_PDF', $newModel, 'chaine', 0, '', $conf->entity); + } + header('Location: '.$_SERVER['PHP_SELF']); + exit; +} +if ($action === 'set' && $user->hasRight('mahnung', 'setup')) { + $newModel = GETPOST('value', 'alphanohtml'); + $sql = "INSERT INTO ".MAIN_DB_PREFIX."document_model (nom, type, entity, libelle) VALUES ('".$db->escape($newModel)."', 'mahnung', ".((int) $conf->entity).", '".$db->escape($newModel)."')"; + $db->query($sql); + header('Location: '.$_SERVER['PHP_SELF']); + exit; +} +if ($action === 'del' && $user->hasRight('mahnung', 'setup')) { + $newModel = GETPOST('value', 'alphanohtml'); + $sql = "DELETE FROM ".MAIN_DB_PREFIX."document_model WHERE nom = '".$db->escape($newModel)."' AND type = 'mahnung' AND entity = ".((int) $conf->entity); + $db->query($sql); + if (getDolGlobalString('MAHNUNG_ADDON_PDF') === $newModel) { + dolibarr_del_const($db, 'MAHNUNG_ADDON_PDF', $conf->entity); + } + header('Location: '.$_SERVER['PHP_SELF']); + exit; +} + // --------------------------------------------------------------- // View // --------------------------------------------------------------- @@ -278,5 +309,91 @@ print ''; print '
'; print ''; +// --- Block: Dokumentenmodelle ----------------------------------------------------------- +print '

'; +print load_fiche_titre($langs->trans('MahnungDokumentModelle'), 'Verfuegbare Template-Variablen', ''); + +// Aktionen fuer Dokumentenmodelle (Upload, Loeschen, setModuleOptions) +include_once DOL_DOCUMENT_ROOT.'/core/actions_setmoduleoptions.inc.php'; + +// Dokumentenmodelle auflisten +$def = array(); +$sql = "SELECT nom FROM ".MAIN_DB_PREFIX."document_model WHERE type = 'mahnung' AND entity = ".((int) $conf->entity); +$resql = $db->query($sql); +if ($resql) { + while ($obj = $db->fetch_object($resql)) { + $def[] = $obj->nom; + } + $db->free($resql); +} + +// Verfuegbare Modelle scannen (aus den doc/-Klassen) +$dirmodels = array( + DOL_DOCUMENT_ROOT.'/custom/mahnung/core/modules/mahnung/doc/', +); +$modellist = array(); +foreach ($dirmodels as $dmod) { + $files = dol_dir_list($dmod, 'files', 0, '\.modules\.php$'); + foreach ($files as $file) { + $classname = preg_replace('/\.modules\.php$/', '', $file['name']); + // Modellname ohne doc_/pdf_-Prefix (commonGenerateDocument fuegt den Prefix selbst hinzu) + $modelname = preg_replace('/^(doc|pdf)_/', '', $classname); + include_once $file['fullname']; + if (class_exists($classname)) { + $obj = new $classname($db); + $modellist[$modelname] = $obj; + } + } +} + +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; + +foreach ($modellist as $mname => $mobj) { + print ''; + print ''; + print ''; + + // Status (aktiviert/deaktiviert) + print ''; + + // Default + print ''; + + // Info (ODT: Upload-Formular) + print ''; + print ''; +} + +print '
'.$langs->trans('Name').''.$langs->trans('Description').''.$langs->trans('Status').''.$langs->trans('Default').'
'.dol_escape_htmltag($mobj->name).''.dol_escape_htmltag($mobj->description ?? '').''; + if (in_array($mname, $def)) { + print ''; + print img_picto($langs->trans('Activated'), 'switch_on'); + print ''; + } else { + print ''; + print img_picto($langs->trans('Disabled'), 'switch_off'); + print ''; + } + print ''; + if (getDolGlobalString('MAHNUNG_ADDON_PDF') === $mname) { + print img_picto($langs->trans('Default'), 'on'); + } elseif (in_array($mname, $def)) { + print ''; + print img_picto($langs->trans('SetAsDefault'), 'off'); + print ''; + } + print ''; + if (method_exists($mobj, 'info')) { + print $mobj->info($langs); + } + print '
'; + llxFooter(); $db->close(); diff --git a/admin/templatevars.php b/admin/templatevars.php new file mode 100644 index 0000000..792f193 --- /dev/null +++ b/admin/templatevars.php @@ -0,0 +1,220 @@ + + * + * 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 mahnung/admin/templatevars.php + * \ingroup mahnung + * \brief Uebersicht aller verfuegbaren Variablen fuer ODT-Templates. + */ + +$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 && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php")) { + $res = @include dirname(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"); +} + +global $langs, $user; +$langs->loadLangs(array('admin', 'mahnung@mahnung')); + +if (!$user->admin && !$user->hasRight('mahnung', 'setup')) { + accessforbidden(); +} + +llxHeader('', 'Mahnung — Template-Variablen'); + +print load_fiche_titre('Verfuegbare Variablen fuer ODT-Templates', 'Zurueck zum Setup', 'fa-envelope-open-text'); + +print '
'; +print 'Diese Variablen koennen in ODT-Templates mit geschweiften Klammern verwendet werden, z.B. {mahnung_ref}.
'; +print 'Stufen-spezifische Templates: mahnung_stufe1.odt, mahnung_stufe2.odt, mahnung_stufe3.odt — Fallback: beliebiges .odt im Template-Verzeichnis.'; +print '
'; + +// Mahnung-Variablen +print ''; +print ''; +print ''; + +$mahnungVars = array( + array('{mahnung_ref}', 'Mahnung-Referenznummer', 'MAHN2026-0001'), + array('{mahnung_stufe}', 'Mahnstufe (Nummer)', '1'), + array('{mahnung_stufe_label}', 'Bezeichnung der Mahnstufe', 'Zahlungserinnerung'), + array('{mahnung_date}', 'Datum der Mahnung', '10.05.2026'), + array('{mahnung_date_lim_alt}', 'Urspruengliches Faelligkeitsdatum der Rechnung', '25.04.2026'), + array('{mahnung_date_lim_neu}', 'Neue Zahlungsfrist', '24.05.2026'), + array('{mahnung_betrag_offen}', 'Offener Rechnungsbetrag', '131,34'), + array('{mahnung_mahngebuehr}', 'Mahngebuehr dieser Stufe', '5,00'), + array('{mahnung_pauschale_b2b}', 'B2B-Pauschale nach BGB §288 Abs. 5', '40,00'), + array('{mahnung_verzugszinsen}', 'Berechnete Verzugszinsen', '1,23'), + array('{mahnung_summe}', 'Gesamtforderung (offen + Gebuehren + Zinsen)', '177,57'), + array('{mahnung_basiszins}', 'BGB-Basiszinssatz (Snapshot bei Erstellung)', '1,27'), + array('{mahnung_zinssatz}', 'Effektiver Zinssatz (Basis + Aufschlag)', '6,27'), + array('{mahnung_kundentyp}', 'Kundentyp', 'B2C oder B2B'), + array('{mahnung_versandart}', 'Versandart', 'pdf, mail, druck, none'), + array('{mahnung_pdf_intro}', 'Einleitungstext der Mahnstufe (aus Setup oder Default)', 'unsere unten aufgefuehrte Rechnung...'), +); + +foreach ($mahnungVars as $v) { + print ''; + print ''; + print ''; + print ''; + print ''; +} +print '
Mahnung
VariableBeschreibungBeispiel
'.dol_escape_htmltag($v[0]).''.dol_escape_htmltag($v[1]).''.dol_escape_htmltag($v[2]).'
'; + +// Rechnungs-Variablen +print '
'; +print ''; +print ''; +print ''; + +$factureVars = array( + array('{facture_ref}', 'Rechnungsnummer', 'IN2604-0036'), + array('{facture_date}', 'Rechnungsdatum', '01.04.2026'), + array('{facture_date_lim}', 'Original-Faelligkeitsdatum', '25.04.2026'), + array('{facture_total_ht}', 'Nettobetrag der Rechnung', '110,37'), + array('{facture_total_ttc}', 'Bruttobetrag der Rechnung', '131,34'), + array('{facture_total_tva}', 'MwSt-Betrag', '20,97'), + array('{facture_already_paid}', 'Bereits gezahlter Betrag', '0,00'), +); + +foreach ($factureVars as $v) { + print ''; + print ''; + print ''; + print ''; + print ''; +} +print '
Verknuepfte Rechnung
VariableBeschreibungBeispiel
'.dol_escape_htmltag($v[0]).''.dol_escape_htmltag($v[1]).''.dol_escape_htmltag($v[2]).'
'; + +// Firmen-Variablen (Absender) +print '
'; +print ''; +print ''; +print ''; + +$mysocVars = array( + array('{mycompany_name}', 'Firmenname', 'Alles Watt Laeuft'), + array('{mycompany_address}', 'Strasse', 'Musterstrasse 1'), + array('{mycompany_zip}', 'PLZ', '24536'), + array('{mycompany_town}', 'Ort', 'Neumuenster'), + array('{mycompany_country}', 'Land', 'Deutschland'), + array('{mycompany_phone}', 'Telefonnummer', '04321 1234567'), + array('{mycompany_fax}', 'Faxnummer', ''), + array('{mycompany_email}', 'E-Mail-Adresse', 'info@example.de'), + array('{mycompany_web}', 'Webseite', 'www.example.de'), + array('{mycompany_idprof1}', 'Handelsregisternummer', ''), + array('{mycompany_idprof2}', 'SIRET/Steuernummer', ''), + array('{mycompany_capital}', 'Stammkapital', ''), + array('{mycompany_logo}', 'Firmenlogo (wird als Bild eingefuegt)', '(Bilddatei)'), +); + +foreach ($mysocVars as $v) { + print ''; + print ''; + print ''; + print ''; + print ''; +} +print '
Eigene Firma (Absender)
VariableBeschreibungBeispiel
'.dol_escape_htmltag($v[0]).''.dol_escape_htmltag($v[1]).''.dol_escape_htmltag($v[2]).'
'; + +// Kunden-Variablen +print '
'; +print ''; +print ''; +print ''; + +$companyVars = array( + array('{company_name}', 'Kundenname', 'Brigitte Ladewig'), + array('{company_alias}', 'Kurzname/Alias', ''), + array('{company_address}', 'Strasse', 'Beispielweg 5'), + array('{company_zip}', 'PLZ', '24534'), + array('{company_town}', 'Ort', 'Neumuenster'), + array('{company_country}', 'Land', 'Deutschland'), + array('{company_phone}', 'Telefon', '04321 9876543'), + array('{company_email}', 'E-Mail', 'b.ladewig@example.de'), + array('{company_idprof1}', 'Handelsregister', ''), + array('{company_idprof2}', 'Steuernummer', ''), + array('{company_vatnumber}', 'USt-IdNr.', 'DE123456789'), + array('{company_note_public}', 'Oeffentliche Notiz des Kunden', ''), +); + +foreach ($companyVars as $v) { + print ''; + print ''; + print ''; + print ''; + print ''; +} +print '
Kunde (Empfaenger)
VariableBeschreibungBeispiel
'.dol_escape_htmltag($v[0]).''.dol_escape_htmltag($v[1]).''.dol_escape_htmltag($v[2]).'
'; + +// Bank-Variablen +print '
'; +print ''; +print ''; +print ''; + +$bankVars = array( + array('{mahnung_bank_label}', 'Name der Bank', 'Sparkasse Suedholstein'), + array('{mahnung_bank_iban}', 'IBAN', 'DE89 3704 0044 0532 0130 00'), + array('{mahnung_bank_bic}', 'BIC/SWIFT', 'COBADEFFXXX'), +); + +foreach ($bankVars as $v) { + print ''; + print ''; + print ''; + print ''; + print ''; +} +print '
Bankverbindung
VariableBeschreibungBeispiel
'.dol_escape_htmltag($v[0]).''.dol_escape_htmltag($v[1]).''.dol_escape_htmltag($v[2]).'
'; + +// Dolibarr-Standard-Variablen +print '
'; +print ''; +print ''; +print ''; + +$stdVars = array( + array('{__FROM_NAME__}', 'Absender-Name', 'Alles Watt Laeuft'), + array('{__FROM_EMAIL__}', 'Absender-E-Mail', 'info@example.de'), + array('{__DATE__}', 'Aktuelles Datum', '10.05.2026'), + array('{myuser_lastname}', 'Nachname des eingeloggten Users', 'Wisch'), + array('{myuser_firstname}', 'Vorname des eingeloggten Users', 'Eduard'), + array('{myuser_email}', 'E-Mail des eingeloggten Users', 'data@example.de'), +); + +foreach ($stdVars as $v) { + print ''; + print ''; + print ''; + print ''; + print ''; +} +print '
Dolibarr-Standard (Auswahl)
VariableBeschreibungBeispiel
'.dol_escape_htmltag($v[0]).''.dol_escape_htmltag($v[1]).''.dol_escape_htmltag($v[2]).'
'; + +llxFooter(); +$db->close(); diff --git a/ajax/createmahnung.php b/ajax/createmahnung.php index 902fff3..6e312aa 100644 --- a/ajax/createmahnung.php +++ b/ajax/createmahnung.php @@ -28,7 +28,6 @@ require_once $_SERVER['DOCUMENT_ROOT'].'/main.inc.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/class/mahnungvorschlag.class.php'; -require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/class/mahnungpdf.class.php'; require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; @@ -101,7 +100,6 @@ $forceStufe = ($forceStufe >= 1 && $forceStufe <= 3) ? $forceStufe : 0; // 4) Verarbeitung — pro Rechnung Vorschlag holen, Mahnung erzeugen, PDF generieren $service = new MahnungVorschlag($db); -$pdfGen = new MahnungPdf($db); $basiszins = (float) getDolGlobalString('MAHNUNG_BASISZINS', '1.27'); $created = 0; @@ -172,9 +170,9 @@ foreach ($factureIds as $fid) { continue; } - $pdfPath = $pdfGen->generate($mahnung, $user); - if ($pdfPath === false) { - $failed[] = 'Rechnung #'.$fid.' (Mahnung '.$mahnung->ref.'): PDF-Fehler '.$pdfGen->error; + $docResult = $mahnung->generateDocument('', $langs); + if ($docResult <= 0) { + $failed[] = 'Rechnung #'.$fid.' (Mahnung '.$mahnung->ref.'): Dokument-Fehler '.$mahnung->error; continue; } diff --git a/card.php b/card.php index 1505386..9e95bb2 100644 --- a/card.php +++ b/card.php @@ -57,17 +57,26 @@ if ($mahnung->fetch($id) <= 0) { // Stornieren if ($action === 'storno' && $user->hasRight('mahnung', 'delete')) { - if (!verifCsrf($_POST['token'] ?? '', 'mahnung_storno')) { - setEventMessages($langs->trans('ErrorBadValueForToken'), null, 'errors'); - } else { - $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'); + $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('Dokument erstellt', null, 'mesgs'); + } else { + setEventMessages('Dokument-Fehler: '.$mahnung->error, null, 'errors'); + } + header('Location: '.$_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id)); + exit; } llxHeader('', $langs->trans('MahnungRef').' '.$mahnung->ref); @@ -103,18 +112,80 @@ if ((float) $mahnung->pauschale_b2b > 0) { print ''.$langs->trans('MahnungVerzugszinsen').''.price($mahnung->verzugszinsen).' (Basiszins '.number_format((float) $mahnung->basiszins_snapshot, 2, ',', '.').' %)'; print ''.$langs->trans('MahnungSumme').''.price($mahnung->summe_mahnung).''; print 'Status'.dol_escape_htmltag($mahnung->getStatusLabel()).''; -if (!empty($mahnung->pdf_path)) { - $relativePdf = str_replace(DOL_DATA_ROOT, '', $mahnung->pdf_path); - $dl = DOL_URL_ROOT.'/document.php?modulepart=facture&file='.urlencode(ltrim(str_replace('/facture/', '', $relativePdf), '/')); - print 'PDFPDF herunterladen'; -} print ''; -// Aktionen -if ($mahnung->status !== Mahnung::STATUS_STORNIERT && $user->hasRight('mahnung', 'delete')) { - require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php'; - $form = new Form($db); +// --- Generierte Dokumente (wie bei Rechnungen) --- +print '
'; +print load_fiche_titre($langs->trans('Documents'), '', 'fa-file'); +// Dokumente im Rechnungsordner suchen die zur Mahnung gehoeren +require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; +$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); +} + +$mahnungRef = dol_sanitizeFileName($mahnung->ref); +$fileList = array(); +if (!empty($docDir) && is_dir($docDir)) { + $allFiles = dol_dir_list($docDir, 'files', 0, preg_quote($mahnungRef, '/')); + foreach ($allFiles as $f) { + $fileList[] = $f; + } +} + +if (!empty($fileList)) { + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + 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'; // Inline-Ansicht (keine Download-Erzwingung) + $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 ''; + // Dateiname mit Icon + print ''; + // Groesse + print ''; + // Datum + print ''; + // Aktionen: Vorschau + Download + print ''; + print ''; + } + print '
'.$langs->trans('Document').''.$langs->trans('Size').''.$langs->trans('Date').'
'; + print ''; + print img_picto('', $icon, 'class="pictofixedwidth"'); + print dol_escape_htmltag($fname); + print ''; + print ''.dol_print_size($filesize, 0, 0).''.dol_print_date($filedate, 'dayhour').''; + if ($ext === 'pdf') { + print ''.img_picto($langs->trans('Preview'), 'search', 'class="pictofixedwidth"').' '; + } + print ''.img_picto($langs->trans('Download'), 'download', 'class="pictofixedwidth"').''; + print '
'; +} else { + print '
'.$langs->trans('NoDocuments').'
'; +} + +// Aktionen +require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php'; +$form = new Form($db); + +if ($mahnung->status !== Mahnung::STATUS_STORNIERT && $user->hasRight('mahnung', 'delete')) { if ($action === 'confirm_storno') { print $form->formconfirm( $_SERVER['PHP_SELF'].'?id='.((int) $mahnung->id), @@ -126,12 +197,33 @@ if ($mahnung->status !== Mahnung::STATUS_STORNIERT && $user->hasRight('mahnung', 1 ); } +} - print '
'; +print '
'; +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 ''; + } + + print ''; + print $langs->trans('MahnungGenerate'); + print ' '; +} +if ($mahnung->status !== Mahnung::STATUS_STORNIERT && $user->hasRight('mahnung', 'delete')) { print ''; print $langs->trans('MahnungStornieren'); - print '
'; + print ''; } +print '
'; llxFooter(); $db->close(); diff --git a/class/mahnung.class.php b/class/mahnung.class.php index a04033f..242bb9c 100644 --- a/class/mahnung.class.php +++ b/class/mahnung.class.php @@ -92,6 +92,9 @@ class Mahnung extends CommonObject /** @var string */ public $note_private; + /** @var string Aktuelles Dokumentenmodell */ + public $model_pdf; + /** @var int 0..9 */ public $status = self::STATUS_ENTWURF; @@ -250,10 +253,44 @@ class Mahnung extends CommonObject $this->fk_user_creat = $obj->fk_user_creat; $this->fk_user_modif = $obj->fk_user_modif; + // Alias fuer CommonObject::fetch_thirdparty() + $this->socid = $this->fk_soc; + $this->db->free($resql); return 1; } + /** + * Dokument generieren ueber das Dolibarr-Dokumentenmodell-System. + * + * @param string $modele Modellname ('standard_mahnung', 'generic_mahnung_odt') + * @param Translate $outputlangs Ausgabesprache + * @param int $hidedetails Details ausblenden + * @param int $hidedesc Beschreibung ausblenden + * @param int $hideref Referenz ausblenden + * @param array $moreparams Weitere Parameter + * @return int >0 OK, <=0 Fehler + */ + public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null) + { + global $conf, $langs; + + $langs->load("mahnung@mahnung"); + + if (!dol_strlen($modele)) { + $modele = 'standard_mahnung'; + if (!empty($this->model_pdf)) { + $modele = $this->model_pdf; + } elseif (getDolGlobalString('MAHNUNG_ADDON_PDF')) { + $modele = getDolGlobalString('MAHNUNG_ADDON_PDF'); + } + } + + $modelpath = "core/modules/mahnung/doc/"; + + return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams); + } + /** * Mehrere Mahnungen laden. * diff --git a/core/boxes/box_mahnung_offen.php b/core/boxes/box_mahnung_offen.php new file mode 100644 index 0000000..6b9e1a2 --- /dev/null +++ b/core/boxes/box_mahnung_offen.php @@ -0,0 +1,254 @@ + + * + * 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/core/boxes/box_mahnung_offen.php + * \ingroup mahnung + * \brief Widget: Aelteste offene Kundenrechnungen mit Mahnstufe. + */ + +require_once DOL_DOCUMENT_ROOT.'/core/boxes/modules_boxes.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; +require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; + +/** + * Widget: Offene Kundenrechnungen mit aktueller Mahnstufe. + */ +class box_mahnung_offen extends ModeleBoxes +{ + public $boxcode = "mahnungoffenerechnungen"; + public $boximg = "fa-envelope-open-text"; + public $boxlabel = "MahnungBoxOffeneRechnungen"; + public $depends = array("facture", "mahnung"); + + /** + * @param DoliDB $db + * @param string $param + */ + public function __construct($db, $param) + { + global $user; + $this->db = $db; + $this->hidden = !($user->hasRight('facture', 'lire')); + } + + /** + * @param int $max + */ + public function loadBox($max = 5) + { + global $conf, $user, $langs; + + $this->max = $max; + + $langs->loadLangs(array('bills', 'mahnung@mahnung')); + + $facturestatic = new Facture($this->db); + $societestatic = new Societe($this->db); + + $this->info_box_head = array( + 'text' => $langs->trans("MahnungBoxOffeneRechnungen", $this->max), + ); + + if (!$user->hasRight('facture', 'lire')) { + $this->info_box_contents[0][0] = array( + 'td' => 'class="nohover left"', + 'text' => ''.$langs->trans("ReadPermissionNotAllowed").'' + ); + return; + } + + // Offene Rechnungen mit optionalem LEFT JOIN auf letzte aktive Mahnung + $sql = "SELECT s.rowid as socid, s.nom as name, s.logo, s.email, s.entity,"; + $sql .= " s.code_client, s.client,"; + $sql .= " f.rowid as facid, f.ref, f.type, f.datef as date,"; + $sql .= " f.date_lim_reglement as datelimit,"; + $sql .= " f.total_ht, f.total_tva, f.total_ttc,"; + $sql .= " f.paye, f.fk_statut as status,"; + $sql .= " COALESCE(SUM(pf.amount), 0) as am,"; + // Letzte Mahnstufe per Subquery + $sql .= " (SELECT m2.stufe FROM ".MAIN_DB_PREFIX."mahnung_mahnung as m2"; + $sql .= " WHERE m2.fk_facture = f.rowid AND m2.status NOT IN (".Mahnung::STATUS_STORNIERT.")"; + $sql .= " ORDER BY m2.stufe DESC LIMIT 1) as mahnstufe,"; + $sql .= " (SELECT m3.date_mahnung FROM ".MAIN_DB_PREFIX."mahnung_mahnung as m3"; + $sql .= " WHERE m3.fk_facture = f.rowid AND m3.status NOT IN (".Mahnung::STATUS_STORNIERT.")"; + $sql .= " ORDER BY m3.stufe DESC LIMIT 1) as mahndatum"; + $sql .= " FROM ".MAIN_DB_PREFIX."facture as f"; + $sql .= " INNER JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid = f.fk_soc"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiement_facture as pf ON f.rowid = pf.fk_facture"; + $sql .= " WHERE f.entity IN (".getEntity('invoice').")"; + $sql .= " AND f.paye = 0"; + $sql .= " AND f.fk_statut = 1"; + $sql .= " AND f.date_lim_reglement IS NOT NULL"; + $sql .= " AND f.date_lim_reglement < '".$this->db->idate(dol_now())."'"; + if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) { + $sql .= " AND s.rowid IN (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_user = ".((int) $user->id).")"; + } + if ($user->socid) { + $sql .= " AND s.rowid = ".((int) $user->socid); + } + $sql .= " GROUP BY s.rowid, s.nom, s.logo, s.email, s.entity, s.code_client, s.client,"; + $sql .= " f.rowid, f.ref, f.type, f.datef, f.date_lim_reglement,"; + $sql .= " f.total_ht, f.total_tva, f.total_ttc, f.paye, f.fk_statut"; + $sql .= " ORDER BY f.date_lim_reglement ASC, f.ref ASC"; + $sql .= $this->db->plimit($this->max + 1, 0); + + require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/class/mahnung.class.php'; + + $result = $this->db->query($sql); + if (!$result) { + $this->info_box_contents[0][0] = array( + 'td' => '', 'maxlength' => 500, + 'text' => $this->db->error().' sql='.$sql, + ); + return; + } + + $num = $this->db->num_rows($result); + $line = 0; + $l_due_date = $langs->trans('Late').' ('.strtolower($langs->trans('DateDue')).': %s)'; + $totalHt = 0; + + while ($line < min($num, $this->max)) { + $objp = $this->db->fetch_object($result); + + $datelimit = $this->db->jdate($objp->datelimit); + $tageVerzug = (int) floor((dol_now() - $datelimit) / 86400); + + $facturestatic->id = $objp->facid; + $facturestatic->ref = $objp->ref; + $facturestatic->type = $objp->type; + $facturestatic->total_ht = $objp->total_ht; + $facturestatic->total_tva = $objp->total_tva; + $facturestatic->total_ttc = $objp->total_ttc; + $facturestatic->date = $this->db->jdate($objp->date); + $facturestatic->date_lim_reglement = $datelimit; + $facturestatic->statut = $objp->status; + $facturestatic->status = $objp->status; + $facturestatic->paye = $objp->paye; + $facturestatic->paid = $objp->paye; + $facturestatic->alreadypaid = $objp->am; + $facturestatic->totalpaid = $objp->am; + + $societestatic->id = $objp->socid; + $societestatic->name = $objp->name; + $societestatic->code_client = $objp->code_client; + $societestatic->client = $objp->client; + $societestatic->logo = $objp->logo; + $societestatic->email = $objp->email; + $societestatic->entity = $objp->entity; + + $late = ''; + if ($facturestatic->hasDelay()) { + $late = img_warning(sprintf($l_due_date, dol_print_date($datelimit, 'day', 'tzuserrel'))); + } + + // Mahnstufe-Badge + $mahnBadge = ''; + if (!empty($objp->mahnstufe)) { + $stufe = (int) $objp->mahnstufe; + $colors = array(1 => '#4a90d9', 2 => '#e68a00', 3 => '#cc3333'); + $labels = array(1 => 'Stufe 1', 2 => 'Stufe 2', 3 => 'Stufe 3'); + $color = $colors[$stufe] ?? '#666'; + $label = $labels[$stufe] ?? 'Stufe '.$stufe; + $mahnDatum = $objp->mahndatum ? dol_print_date($this->db->jdate($objp->mahndatum), 'day') : ''; + $tooltip = $label.($mahnDatum ? ' vom '.$mahnDatum : ''); + $mahnBadge = ' '.$label.''; + } + + // Spalte 1: Rechnung + Warnung + $this->info_box_contents[$line][] = array( + 'td' => 'class="nowraponall"', + 'text' => $facturestatic->getNomUrl(1), + 'text2' => $late, + 'asis' => 1, + ); + + // Spalte 2: Kunde + $this->info_box_contents[$line][] = array( + 'td' => 'class="tdoverflowmax150 maxwidth150onsmartphone"', + 'text' => $societestatic->getNomUrl(1, '', 44), + 'asis' => 1, + ); + + // Spalte 3: Betrag + $this->info_box_contents[$line][] = array( + 'td' => 'class="nowraponall right amount"', + 'text' => price($objp->total_ht, 0, $langs, 0, -1, -1, $conf->currency), + ); + + // Spalte 4: Faelligkeitsdatum + $this->info_box_contents[$line][] = array( + 'td' => 'class="center nowraponall" title="'.dol_escape_htmltag($langs->trans("DateDue").': '.dol_print_date($datelimit, 'day', 'tzuserrel')).'"', + 'text' => dol_print_date($datelimit, 'day', 'tzuserrel'), + ); + + // Spalte 5: Tage Verzug + $this->info_box_contents[$line][] = array( + 'td' => 'class="center nowraponall"', + 'text' => $tageVerzug.' T.', + ); + + // Spalte 6: Mahnstufe + $this->info_box_contents[$line][] = array( + 'td' => 'class="center nowraponall"', + 'text' => !empty($mahnBadge) ? $mahnBadge : '', + 'asis' => 1, + ); + + $totalHt += (float) $objp->total_ht; + $line++; + } + + if ($this->max < $num) { + $this->info_box_contents[$line][] = array('td' => 'colspan="6"', 'text' => '...'); + $line++; + } + + if ($num > 0) { + // Summenzeile + $this->info_box_contents[$line][] = array( + 'tr' => 'class="liste_total"', + 'td' => 'class="liste_total"', + 'text' => $langs->trans("Total"), + ); + $this->info_box_contents[$line][] = array( + 'td' => 'class="liste_total"', + 'text' => ' ', + ); + $this->info_box_contents[$line][] = array( + 'td' => 'class="nowraponall right liste_total"', + 'text' => price($totalHt, 0, $langs, 0, -1, -1, $conf->currency), + ); + $this->info_box_contents[$line][] = array( + 'td' => 'class="liste_total"', + 'text' => ' ', + ); + $this->info_box_contents[$line][] = array( + 'td' => 'class="liste_total"', + 'text' => ' ', + ); + $this->info_box_contents[$line][] = array( + 'td' => 'class="liste_total"', + 'text' => ' ', + ); + } + + $this->db->free($result); + } + + /** + * @param array|null $head + * @param array|null $contents + * @param int $nooutput + * @return string + */ + public function showBox($head = null, $contents = null, $nooutput = 0) + { + return parent::showBox($this->info_box_head, $this->info_box_contents, $nooutput); + } +} diff --git a/core/modules/mahnung/doc/doc_generic_mahnung_odt.modules.php b/core/modules/mahnung/doc/doc_generic_mahnung_odt.modules.php new file mode 100644 index 0000000..9f3d6b0 --- /dev/null +++ b/core/modules/mahnung/doc/doc_generic_mahnung_odt.modules.php @@ -0,0 +1,504 @@ + + * + * 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/core/modules/mahnung/doc/doc_generic_mahnung_odt.modules.php + * \ingroup mahnung + * \brief ODT-Template-Generator fuer Mahnschreiben. + * + * Stufen-spezifische Templates: mahnung_stufe1.odt, mahnung_stufe2.odt, mahnung_stufe3.odt + * Fallback: beliebiges hochgeladenes .odt Template. + */ + +require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/core/modules/mahnung/modules_mahnung.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/doc.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; +require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; +require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/class/mahnung.class.php'; +require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/class/mahnungstufe.class.php'; + +/** + * ODT-Template-basierter Dokumentengenerator fuer Mahnungen. + */ +class doc_generic_mahnung_odt extends ModelePDFMahnung +{ + /** + * @var string + */ + public $version = 'dolibarr'; + + /** + * @param DoliDB $db + */ + public function __construct($db) + { + global $langs, $mysoc; + + $langs->loadLangs(array('main', 'companies')); + + $this->db = $db; + $this->name = 'ODT templates'; + $this->description = $langs->trans('DocumentModelOdt'); + $this->scandir = 'MAHNUNG_ADDON_PDF_ODT_PATH'; + + $this->type = 'odt'; + $this->page_largeur = 0; + $this->page_hauteur = 0; + $this->format = array($this->page_largeur, $this->page_hauteur); + $this->marge_gauche = 0; + $this->marge_droite = 0; + $this->marge_haute = 0; + $this->marge_basse = 0; + + $this->option_logo = 1; + $this->option_multilang = 1; + $this->option_freetext = 1; + + if ($mysoc !== null) { + $this->emetteur = $mysoc; + if (!$this->emetteur->country_code) { + $this->emetteur->country_code = substr($langs->defaultlang, -2); + } + } + } + + /** + * Beschreibung und Template-Upload-Formular fuer die Admin-Seite. + * + * @param Translate $langs + * @return string + */ + public function info($langs) + { + global $conf; + + $langs->loadLangs(array('errors', 'companies')); + + $form = new Form($this->db); + + $texte = $this->description.".
\n"; + $texte .= '
'; + $texte .= ''; + $texte .= ''; + $texte .= ''; + $texte .= ''; + $texte .= ''; + + // Verzeichnis-Liste + $texte .= ''; + + $texte .= '
'; + $texttitle = $langs->trans("ListOfDirectories"); + $listofdir = explode(',', preg_replace('/[\r\n]+/', ',', trim(getDolGlobalString('MAHNUNG_ADDON_PDF_ODT_PATH')))); + $listoffiles = array(); + foreach ($listofdir as $key => $tmpdir) { + $tmpdir = trim($tmpdir); + $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir); + if (!$tmpdir) { + unset($listofdir[$key]); + continue; + } + if (!is_dir($tmpdir)) { + $texttitle .= img_warning($langs->trans("ErrorDirNotFound", $tmpdir), ''); + } else { + $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '\.(ods|odt)'); + if (count($tmpfiles)) { + $listoffiles = array_merge($listoffiles, $tmpfiles); + } + } + } + $texthelp = $langs->trans("ListOfDirectoriesForModelGenODT"); + $texthelp .= '

'.$langs->trans("ExampleOfDirectoriesForModelGen").''; + $texthelp .= '
'.$langs->trans("FollowingSubstitutionKeysCanBeUsed").'
'; + $texthelp .= '
Mahnung-spezifisch:
'; + $texthelp .= '{mahnung_ref}, {mahnung_stufe}, {mahnung_stufe_label}, {mahnung_date},
'; + $texthelp .= '{mahnung_betrag_offen}, {mahnung_mahngebuehr}, {mahnung_verzugszinsen},
'; + $texthelp .= '{mahnung_summe}, {mahnung_basiszins}, {mahnung_zinssatz}, {mahnung_kundentyp},
'; + $texthelp .= '{mahnung_pdf_intro}, {mahnung_date_lim_alt}, {mahnung_date_lim_neu}
'; + $texthelp .= '
Rechnungsdaten:
'; + $texthelp .= '{facture_ref}, {facture_date}, {facture_total_ttc}, {facture_already_paid}
'; + $texthelp .= '
Bankdaten:
'; + $texthelp .= '{mahnung_bank_label}, {mahnung_bank_iban}, {mahnung_bank_bic}
'; + $texthelp .= '
Stufen-spezifische Templates:
'; + $texthelp .= 'mahnung_stufe1.odt, mahnung_stufe2.odt, mahnung_stufe3.odt
'; + + $texte .= $form->textwithpicto($texttitle, $texthelp, 1, 'help', '', 1, 3, $this->name); + $texte .= '
'; + $texte .= ''; + $texte .= '
'; + $texte .= ''; + $texte .= '
'; + + // Gefundene Templates auflisten + $nbofiles = count($listoffiles); + if (getDolGlobalString('MAHNUNG_ADDON_PDF_ODT_PATH')) { + $texte .= $langs->trans("NumberOfModelFilesFound").': '.count($listoffiles).''; + } + if ($nbofiles) { + $texte .= '
'; + foreach ($listoffiles as $file) { + $texte .= '- '.$file['name'].' '.img_picto('', 'listlight').''; + $texte .= '   '.img_picto('', 'delete').''; + $texte .= '
'; + } + $texte .= '
'; + } + + // Hinweis zur Benennung + $texte .= '
'; + $texte .= 'Dateinamen-Konvention:
'; + $texte .= 'mahnung_stufe1.odt = Zahlungserinnerung (Stufe 1)
'; + $texte .= 'mahnung_stufe2.odt = 1. Mahnung (Stufe 2)
'; + $texte .= 'mahnung_stufe3.odt = Letzte Mahnung (Stufe 3)
'; + $texte .= 'mahnung.odt = Fallback fuer alle Stufen'; + $texte .= '
'; + + // Upload-Feld + $texte .= '
'.$langs->trans("UploadNewTemplate"); + $maxfilesizearray = getMaxFileSizeArray(); + $maxmin = $maxfilesizearray['maxmin']; + if ($maxmin > 0) { + $texte .= ''; + } + $texte .= ' '; + $texte .= ''; + $texte .= ''; + $texte .= '
'; + $texte .= '
'; + $texte .= '
'; + + return $texte; + } + + /** + * Erzeugt das ODT-Dokument aus einem Template. + * + * @param object $object Mahnung-Objekt + * @param Translate $outputlangs Ausgabesprache + * @param string $srctemplatepath Pfad zum ODT-Template + * @param int $hidedetails Details ausblenden + * @param int $hidedesc Beschreibung ausblenden + * @param int $hideref Referenz ausblenden + * @return int 1=OK, <=0=Fehler + */ + public function write_file($object, $outputlangs, $srctemplatepath = '', $hidedetails = 0, $hidedesc = 0, $hideref = 0) + { + global $user, $langs, $conf, $mysoc, $hookmanager; + + if (empty($srctemplatepath)) { + dol_syslog("doc_generic_mahnung_odt::write_file srctemplatepath leer", LOG_WARNING); + return -1; + } + + if (!is_object($hookmanager)) { + include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; + $hookmanager = new HookManager($this->db); + } + $hookmanager->initHooks(array('odtgeneration')); + global $action; + + if (!is_object($outputlangs)) { + $outputlangs = $langs; + } + $sav_charset_output = $outputlangs->charset_output; + $outputlangs->charset_output = 'UTF-8'; + $outputlangs->loadLangs(array('main', 'dict', 'companies', 'bills', 'mahnung@mahnung')); + + $mahnung = $object; + + // Rechnung + Kunde + Stufe laden + $facture = new Facture($this->db); + if ($facture->fetch((int) $mahnung->fk_facture) <= 0) { + $this->error = 'Rechnung '.((int) $mahnung->fk_facture).' nicht ladbar.'; + return -1; + } + + $societe = new Societe($this->db); + if ($societe->fetch((int) $mahnung->fk_soc) <= 0) { + $this->error = 'Kunde '.((int) $mahnung->fk_soc).' nicht ladbar.'; + return -1; + } + // Fuer die Standard-Substitution-Arrays + $mahnung->thirdparty = $societe; + + $stufeObj = new MahnungStufe($this->db); + $stufeObj->fetchByStufe((int) $mahnung->stufe); + + // Stufen-spezifisches Template suchen + $srctemplatepath = $this->findStufeTemplate($srctemplatepath, (int) $mahnung->stufe); + + // Ausgabe-Verzeichnis: im Facture-Ordner (damit es im Rechnung-Tab sichtbar ist) + $dirFact = !empty($conf->facture->multidir_output[$facture->entity]) + ? $conf->facture->multidir_output[$facture->entity] + : $conf->facture->dir_output; + $dir = $dirFact.'/'.dol_sanitizeFileName($facture->ref); + + if (!file_exists($dir)) { + if (dol_mkdir($dir) < 0) { + $this->error = $outputlangs->transnoentities("ErrorCanNotCreateDir", $dir); + return -1; + } + } + + // Dateiname + $newfile = basename($srctemplatepath); + $newfiletmp = preg_replace('/\.od[ts]/i', '', $newfile); + $newfiletmp = preg_replace('/template_/i', '', $newfiletmp); + $newfiletmp = preg_replace('/modele_/i', '', $newfiletmp); + $objectref = dol_sanitizeFileName($mahnung->ref); + $newfiletmp = $objectref.'_'.$newfiletmp; + + $newfileformat = substr($newfile, strrpos($newfile, '.') + 1); + if (getDolGlobalString('MAIN_DOC_USE_TIMING')) { + $format = getDolGlobalString('MAIN_DOC_USE_TIMING'); + if ($format == '1') { + $format = '%Y%m%d%H%M%S'; + } + $filename = $newfiletmp.'-'.dol_print_date(dol_now(), $format).'.'.$newfileformat; + } else { + $filename = $newfiletmp.'.'.$newfileformat; + } + $file = $dir.'/'.$filename; + + // Temp-Verzeichnis + $tmpdir = $conf->mahnung->dir_temp ?? ($conf->facture->dir_temp ?? DOL_DATA_ROOT.'/mahnung/temp'); + dol_mkdir($tmpdir); + + // ODT oeffnen + require_once ODTPHP_PATH.'odf.php'; + try { + $odfHandler = new Odf( + $srctemplatepath, + array( + 'PATH_TO_TMP' => $tmpdir, + 'ZIP_PROXY' => getDolGlobalString('MAIN_ODF_ZIP_PROXY', 'PclZipProxy'), + 'DELIMITER_LEFT' => '{', + 'DELIMITER_RIGHT' => '}' + ) + ); + } catch (Exception $e) { + $this->error = $e->getMessage(); + dol_syslog($e->getMessage(), LOG_ERR); + return -1; + } + + // Substitution-Arrays zusammenbauen + $substitutionarray = array( + '__FROM_NAME__' => $this->emetteur->name, + '__FROM_EMAIL__' => $this->emetteur->email, + ); + complete_substitutions_array($substitutionarray, $outputlangs, $mahnung); + + // Standard-Arrays + $array_common = getCommonSubstitutionArray($outputlangs, 0, null, $mahnung); + $array_object_from_properties = $this->get_substitutionarray_each_var_object($mahnung, $outputlangs); + $array_objet = $this->get_substitutionarray_object($mahnung, $outputlangs); + $array_user = $this->get_substitutionarray_user($user, $outputlangs); + $array_soc = $this->get_substitutionarray_mysoc($mysoc, $outputlangs); + $array_thirdparty = $this->get_substitutionarray_thirdparty($societe, $outputlangs); + $array_other = $this->get_substitutionarray_other($outputlangs); + + // Mahnung-spezifische Tags + $array_mahnung = $this->getMahnungSubstitutionArray($mahnung, $facture, $stufeObj, $outputlangs); + + // Alles zusammenfuehren + $tmparray = array_merge($substitutionarray, $array_common, $array_object_from_properties, $array_user, $array_soc, $array_thirdparty, $array_objet, $array_other, $array_mahnung); + complete_substitutions_array($tmparray, $outputlangs, $mahnung); + + // Hook + $parameters = array('odfHandler' => &$odfHandler, 'file' => $file, 'object' => $mahnung, 'outputlangs' => $outputlangs, 'substitutionarray' => &$tmparray); + $hookmanager->executeHooks('ODTSubstitution', $parameters, $this, $action); + + // Variablen ins ODT schreiben + foreach ($tmparray as $key => $value) { + try { + if (preg_match('/logo$/', $key)) { + if (file_exists($value)) { + $odfHandler->setImage($key, $value); + } else { + $odfHandler->setVars($key, 'ErrorFileNotFound', true, 'UTF-8'); + } + } else { + $odfHandler->setVars($key, $value, true, 'UTF-8'); + } + } catch (OdfException $e) { + dol_syslog($e->getMessage(), LOG_INFO); + } + } + + // Uebersetzungs-Labels + $tmparray2 = $outputlangs->get_translations_for_substitutions(); + foreach ($tmparray2 as $key => $value) { + try { + $odfHandler->setVars($key, $value, true, 'UTF-8'); + } catch (OdfException $e) { + dol_syslog($e->getMessage(), LOG_INFO); + } + } + + // Speichern (als PDF wenn MAIN_ODT_AS_PDF gesetzt) + if (getDolGlobalString('MAIN_ODT_AS_PDF')) { + try { + $odfHandler->exportAsAttachedPDF($file); + } catch (Exception $e) { + $this->error = $e->getMessage(); + dol_syslog($e->getMessage(), LOG_ERR); + return -1; + } + } else { + try { + $odfHandler->saveToDisk($file); + } catch (Exception $e) { + $this->error = $e->getMessage(); + dol_syslog($e->getMessage(), LOG_ERR); + return -1; + } + } + + dolChmod($file); + $odfHandler = null; + + // Pfad in DB persistieren + $mahnung->pdf_path = $file; + $mahnung->update($user); + + $this->result = array('fullpath' => $file); + $outputlangs->charset_output = $sav_charset_output; + + return 1; + } + + /** + * Sucht ein stufen-spezifisches Template (mahnung_stufe{N}.odt). + * Fallback: das uebergebene generische Template. + * + * @param string $srctemplatepath Generisches Template (von commonGenerateDocument) + * @param int $stufe Mahnstufe 1-3 + * @return string Pfad zum besten Template + */ + private function findStufeTemplate($srctemplatepath, $stufe) + { + global $conf; + + $listofdir = explode(',', preg_replace('/[\r\n]+/', ',', trim(getDolGlobalString('MAHNUNG_ADDON_PDF_ODT_PATH')))); + foreach ($listofdir as $tmpdir) { + $tmpdir = trim(preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir)); + if (empty($tmpdir)) { + continue; + } + $stufeFile = $tmpdir.'/mahnung_stufe'.$stufe.'.odt'; + if (file_exists($stufeFile)) { + return $stufeFile; + } + } + + return $srctemplatepath; + } + + /** + * Mahnung-spezifische Substitution-Variablen. + * + * @param Mahnung $mahnung + * @param Facture $facture + * @param MahnungStufe $stufe + * @param Translate $outputlangs + * @return array + */ + private function getMahnungSubstitutionArray($mahnung, $facture, $stufe, $outputlangs) + { + $gezahlt = (float) $facture->total_ttc - (float) $mahnung->betrag_offen; + $basiszins = $mahnung->basiszins_snapshot !== null ? (float) $mahnung->basiszins_snapshot : 0.0; + $aufschlag = $mahnung->customertype === Mahnung::KUNDENTYP_B2B + ? (float) getDolGlobalString('MAHNUNG_AUFSCHLAG_B2B', '9.0') + : (float) getDolGlobalString('MAHNUNG_AUFSCHLAG_B2C', '5.0'); + + $intro = (string) ($stufe->pdf_intro ?? ''); + if (empty($intro)) { + $intro = $this->defaultIntro((int) $mahnung->stufe); + } + + $bank = $this->getDefaultBankData(); + + return array( + 'mahnung_ref' => $mahnung->ref, + 'mahnung_stufe' => (int) $mahnung->stufe, + 'mahnung_stufe_label' => $stufe->label ?? '', + 'mahnung_date' => dol_print_date($mahnung->date_mahnung, 'day'), + 'mahnung_date_lim_alt' => dol_print_date($mahnung->date_lim_reglement_alt, 'day'), + 'mahnung_date_lim_neu' => dol_print_date($mahnung->date_lim_reglement_neu, 'day'), + 'mahnung_betrag_offen' => price((float) $mahnung->betrag_offen), + 'mahnung_mahngebuehr' => price((float) $mahnung->mahngebuehr), + 'mahnung_pauschale_b2b' => price((float) $mahnung->pauschale_b2b), + 'mahnung_verzugszinsen' => price((float) $mahnung->verzugszinsen), + 'mahnung_summe' => price((float) $mahnung->summe_mahnung), + 'mahnung_basiszins' => number_format($basiszins, 2, ',', '.'), + 'mahnung_zinssatz' => number_format($basiszins + $aufschlag, 2, ',', '.'), + 'mahnung_kundentyp' => $mahnung->customertype, + 'mahnung_versandart' => $mahnung->versandart ?? '', + 'mahnung_pdf_intro' => $intro, + // Rechnungsdaten + 'facture_ref' => $facture->ref, + 'facture_date' => dol_print_date($facture->date, 'day'), + 'facture_date_lim' => dol_print_date($facture->date_lim_reglement, 'day'), + 'facture_total_ht' => price((float) $facture->total_ht), + 'facture_total_ttc' => price((float) $facture->total_ttc), + 'facture_total_tva' => price((float) $facture->total_tva), + 'facture_already_paid' => price($gezahlt), + // Bank + 'mahnung_bank_label' => $bank['label'] ?? '', + 'mahnung_bank_iban' => $bank['iban'] ?? '', + 'mahnung_bank_bic' => $bank['bic'] ?? '', + ); + } + + /** + * Standard-Bankkonto-Daten. + * + * @return array + */ + private function getDefaultBankData() + { + $sql = "SELECT label, iban_prefix as iban, bic FROM ".MAIN_DB_PREFIX."bank_account"; + $sql .= " WHERE clos = 0 AND default_rib = 1 LIMIT 1"; + $resql = $this->db->query($sql); + if (!$resql || !$this->db->num_rows($resql)) { + return array(); + } + $obj = $this->db->fetch_object($resql); + $this->db->free($resql); + return array('label' => $obj->label, 'iban' => $obj->iban, 'bic' => $obj->bic); + } + + /** + * Default-Intro je Stufe. + * + * @param int $stufe + * @return string + */ + private function defaultIntro($stufe) + { + switch ((int) $stufe) { + case 1: + return 'unsere unten aufgefuehrte Rechnung ist trotz Ablauf der Zahlungsfrist noch nicht beglichen. ' + . 'Vielleicht ist Ihnen dies entgangen — wir bitten Sie hoeflich, den ausstehenden Betrag zeitnah zu ueberweisen.'; + case 2: + return 'leider mussten wir feststellen, dass die unten aufgefuehrte Rechnung trotz unserer ' + . 'Zahlungserinnerung weiterhin offen ist. Wir bitten Sie nun nachdruecklich um Begleichung ' + . 'des offenen Betrags zuzueglich Verzugszinsen und Mahnkosten.'; + case 3: + default: + return 'wir haben Sie bereits zweimal an die Begleichung der unten aufgefuehrten Rechnung erinnert. ' + . 'Sollte der offene Betrag inkl. Verzugszinsen und Mahnkosten nicht innerhalb der angegebenen Frist ' + . 'auf unserem Konto eingehen, sehen wir uns gezwungen, weitere rechtliche Schritte einzuleiten.'; + } + } +} diff --git a/class/mahnungpdf.class.php b/core/modules/mahnung/doc/pdf_standard_mahnung.modules.php similarity index 67% rename from class/mahnungpdf.class.php rename to core/modules/mahnung/doc/pdf_standard_mahnung.modules.php index 90bf4a6..38424e1 100644 --- a/class/mahnungpdf.class.php +++ b/core/modules/mahnung/doc/pdf_standard_mahnung.modules.php @@ -6,96 +6,107 @@ */ /** - * \file htdocs/custom/mahnung/class/mahnungpdf.class.php + * \file htdocs/custom/mahnung/core/modules/mahnung/doc/pdf_standard_mahnung.modules.php * \ingroup mahnung - * \brief PDF-Generator fuer Mahnschreiben (DIN-5008 Form A). - * - * Nutzt Dolibarrs TCPDF-Wrapper (pdf_getInstance) und schreibt das fertige - * PDF in den Dokumenten-Ordner der Original-Rechnung - * documents/facture/{ref-rechnung}/mahnung-{stufe}-{ref-mahnung}.pdf - * Damit erscheint die Mahnung automatisch im Dokumente-Tab der Rechnung. + * \brief Standard-PDF-Generator (TCPDF, DIN-5008) fuer Mahnschreiben. */ +require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/core/modules/mahnung/modules_mahnung.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php'; require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/class/mahnung.class.php'; require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/class/mahnungstufe.class.php'; -class MahnungPdf +/** + * Standard-PDF-Generator fuer Mahnungen (DIN-5008 Form A, TCPDF). + */ +class pdf_standard_mahnung extends ModelePDFMahnung { - /** @var DoliDB */ - public $db; - - /** @var string */ - public $error = ''; - /** * @param DoliDB $db */ public function __construct($db) { + global $langs; $this->db = $db; + $this->name = 'standard_mahnung'; + $this->description = $langs->trans('MahnungPdfStandard'); + $this->type = 'pdf'; + + $this->page_largeur = 210; + $this->page_hauteur = 297; + $this->format = array($this->page_largeur, $this->page_hauteur); + $this->marge_gauche = 25; + $this->marge_droite = 25; + $this->marge_haute = 25; + $this->marge_basse = 25; } /** - * Erzeugt das PDF zum Mahnvorgang. Setzt $mahnung->pdf_path nach Erfolg - * und schreibt sie via update($user) in die DB. - * - * @param Mahnung $mahnung - * @param User $user - * @return string|false Absoluter Pfad zur PDF-Datei oder false bei Fehler + * @param object $object Mahnung-Objekt + * @param Translate $outputlangs Ausgabesprache + * @param string $srctemplatepath (nicht verwendet bei TCPDF) + * @param int $hidedetails Details ausblenden + * @param int $hidedesc Beschreibung ausblenden + * @param int $hideref Referenz ausblenden + * @return int 1=OK, <=0=Fehler */ - public function generate(Mahnung $mahnung, $user) + public function write_file($object, $outputlangs, $srctemplatepath = '', $hidedetails = 0, $hidedesc = 0, $hideref = 0) { - global $conf, $langs, $mysoc; + global $conf, $langs, $mysoc, $user; - $langs->loadLangs(array('main', 'bills', 'companies', 'mahnung@mahnung')); + if (!is_object($outputlangs)) { + $outputlangs = $langs; + } + $outputlangs->loadLangs(array('main', 'bills', 'companies', 'mahnung@mahnung')); - // Original-Rechnung + Kunde laden + $mahnung = $object; + + // Rechnung + Kunde laden $facture = new Facture($this->db); if ($facture->fetch((int) $mahnung->fk_facture) <= 0) { $this->error = 'Rechnung '.((int) $mahnung->fk_facture).' nicht ladbar.'; - return false; + return -1; } $societe = new Societe($this->db); if ($societe->fetch((int) $mahnung->fk_soc) <= 0) { $this->error = 'Kunde '.((int) $mahnung->fk_soc).' nicht ladbar.'; - return false; + return -1; } $stufeObj = new MahnungStufe($this->db); if ($stufeObj->fetchByStufe((int) $mahnung->stufe) <= 0) { $this->error = 'Mahnstufe '.((int) $mahnung->stufe).' nicht konfiguriert.'; - return false; + return -1; } - // Ziel-Verzeichnis im Doc-Ordner der Rechnung - $dirOutput = $this->getOutputDir($facture); - if (!dol_mkdir($dirOutput)) { + // Ziel-Verzeichnis + $dirOutput = $this->getOutputDir($mahnung, $facture); + if (dol_mkdir($dirOutput) < 0) { $this->error = 'Kann Verzeichnis nicht anlegen: '.$dirOutput; - return false; + return -1; } $filename = 'mahnung-'.((int) $mahnung->stufe).'-'.dol_sanitizeFileName($mahnung->ref).'.pdf'; $absPath = $dirOutput.'/'.$filename; // PDF-Instanz $pdf = pdf_getInstance(array('210', '297')); - $default_font_size = pdf_getPDFFontSize($langs); + $default_font_size = pdf_getPDFFontSize($outputlangs); $pdf->SetAutoPageBreak(true, 25); - $pdf->SetFont(pdf_getPDFFont($langs), '', $default_font_size); + $pdf->SetFont(pdf_getPDFFont($outputlangs), '', $default_font_size); - $pdf->SetTitle($langs->trans('MahnungStufe').' '.((int) $mahnung->stufe).' — '.$facture->ref); - $pdf->SetSubject($langs->trans('MahnungRef').' '.$mahnung->ref); + $pdf->SetTitle($outputlangs->trans('MahnungStufe').' '.((int) $mahnung->stufe).' — '.$facture->ref); + $pdf->SetSubject($outputlangs->trans('MahnungRef').' '.$mahnung->ref); $pdf->SetAuthor((string) $mysoc->name); $pdf->SetCreator('Dolibarr Mahnung-Modul'); $pdf->Open(); $pdf->AddPage(); - $this->renderHeader($pdf, $facture, $societe, $mahnung, $stufeObj); - $this->renderBody($pdf, $facture, $societe, $mahnung, $stufeObj); + $this->renderHeader($pdf, $facture, $societe, $mahnung, $stufeObj, $outputlangs); + $this->renderBody($pdf, $facture, $societe, $mahnung, $stufeObj, $outputlangs); $this->renderFooter($pdf); $pdf->Output($absPath, 'F'); @@ -104,30 +115,22 @@ class MahnungPdf $mahnung->pdf_path = $absPath; $mahnung->update($user); - return $absPath; + $this->result = array('fullpath' => $absPath); + return 1; } /** - * Adressfenster, Datum, Betreff (DIN-5008 Form A: Adressfeld 45mm hoch ab 27mm). - * - * @param TCPDF $pdf - * @param Facture $facture - * @param Societe $societe - * @param Mahnung $mahnung - * @param MahnungStufe $stufe - * @return void + * Adressfenster, Datum, Betreff (DIN-5008 Form A). */ - private function renderHeader($pdf, $facture, $societe, $mahnung, $stufe) + private function renderHeader($pdf, $facture, $societe, $mahnung, $stufe, $outputlangs) { - global $langs, $mysoc; + global $mysoc; - // Absender klein im Adressfeld (Faltmarke darueber, oben in DIN 5008 Adresszeile) $pdf->SetFont('helvetica', '', 7); $pdf->SetXY(25, 50); $senderLine = trim(($mysoc->name ?? '').' · '.($mysoc->address ?? '').' · '.($mysoc->zip ?? '').' '.($mysoc->town ?? '')); $pdf->Cell(85, 4, $senderLine, 0, 1, 'L'); - // Empfaenger-Block (Adressfenster: links 25mm, ab y=55) $pdf->SetFont('helvetica', '', 11); $pdf->SetXY(25, 55); $lines = array(); @@ -149,47 +152,34 @@ class MahnungPdf $pdf->SetX(25); } - // Bezugszeichen-Zeile rechts (DIN-5008): Datum + Mahn-Nr. $pdf->SetFont('helvetica', '', 9); $pdf->SetXY(125, 50); - $pdf->Cell(60, 4, $langs->trans('Date').': '.dol_print_date($mahnung->date_mahnung, 'day'), 0, 1, 'L'); + $pdf->Cell(60, 4, $outputlangs->trans('Date').': '.dol_print_date($mahnung->date_mahnung, 'day'), 0, 1, 'L'); $pdf->SetX(125); - $pdf->Cell(60, 4, $langs->trans('MahnungRef').': '.$mahnung->ref, 0, 1, 'L'); + $pdf->Cell(60, 4, $outputlangs->trans('MahnungRef').': '.$mahnung->ref, 0, 1, 'L'); $pdf->SetX(125); - $pdf->Cell(60, 4, $langs->trans('MahnungRechnung').': '.$facture->ref, 0, 1, 'L'); + $pdf->Cell(60, 4, $outputlangs->trans('MahnungRechnung').': '.$facture->ref, 0, 1, 'L'); - // Betreff $pdf->SetXY(25, 100); $pdf->SetFont('helvetica', 'B', 12); - $betreff = $stufe->label.' — '.$langs->trans('MahnungRechnung').' '.$facture->ref; + $betreff = $stufe->label.' — '.$outputlangs->trans('MahnungRechnung').' '.$facture->ref; $pdf->Cell(0, 6, $betreff, 0, 1, 'L'); } /** - * Anrede, Intro, Tabelle (Rechnung/Datum/Betrag/gezahlt/offen), - * Gebuehrenblock, Gesamtsumme, neue Frist, Bankverbindung. - * - * @param TCPDF $pdf - * @param Facture $facture - * @param Societe $societe - * @param Mahnung $mahnung - * @param MahnungStufe $stufe - * @return void + * Anrede, Intro, Tabelle, Gebuehrenblock, Gesamtsumme, neue Frist. */ - private function renderBody($pdf, $facture, $societe, $mahnung, $stufe) + private function renderBody($pdf, $facture, $societe, $mahnung, $stufe, $outputlangs) { - global $langs, $mysoc; + global $mysoc; $pdf->SetFont('helvetica', '', 11); $pdf->SetXY(25, 110); - // Anrede - $anrede = 'Sehr geehrte Damen und Herren,'; - $pdf->Cell(0, 5, $anrede, 0, 1, 'L'); + $pdf->Cell(0, 5, 'Sehr geehrte Damen und Herren,', 0, 1, 'L'); $pdf->SetX(25); $pdf->Ln(2); - // Intro aus Stufen-Konfig (Fallback Default-Text je Stufe) $intro = (string) $stufe->pdf_intro; if (empty($intro)) { $intro = $this->defaultIntro((int) $mahnung->stufe); @@ -201,11 +191,11 @@ class MahnungPdf // Rechnungs-Tabelle $pdf->SetFont('helvetica', 'B', 10); $pdf->SetX(25); - $pdf->Cell(40, 6, $langs->trans('MahnungRechnung'), 'B', 0, 'L'); - $pdf->Cell(30, 6, $langs->trans('Date'), 'B', 0, 'L'); - $pdf->Cell(30, 6, $langs->trans('TotalTTC'), 'B', 0, 'R'); - $pdf->Cell(30, 6, $langs->trans('AlreadyPaid'), 'B', 0, 'R'); - $pdf->Cell(30, 6, $langs->trans('MahnungBetragOffen'), 'B', 1, 'R'); + $pdf->Cell(40, 6, $outputlangs->trans('MahnungRechnung'), 'B', 0, 'L'); + $pdf->Cell(30, 6, $outputlangs->trans('Date'), 'B', 0, 'L'); + $pdf->Cell(30, 6, $outputlangs->trans('TotalTTC'), 'B', 0, 'R'); + $pdf->Cell(30, 6, $outputlangs->trans('AlreadyPaid'), 'B', 0, 'R'); + $pdf->Cell(30, 6, $outputlangs->trans('MahnungBetragOffen'), 'B', 1, 'R'); $pdf->SetFont('helvetica', '', 10); $pdf->SetX(25); @@ -220,16 +210,16 @@ class MahnungPdf // Gebuehrenblock $pdf->SetX(25); $pdf->SetFont('helvetica', '', 10); - $pdf->Cell(130, 6, $langs->trans('MahnungBetragOffen'), 0, 0, 'L'); + $pdf->Cell(130, 6, $outputlangs->trans('MahnungBetragOffen'), 0, 0, 'L'); $pdf->Cell(30, 6, price((float) $mahnung->betrag_offen).' EUR', 0, 1, 'R'); if ((float) $mahnung->mahngebuehr > 0) { $pdf->SetX(25); - $pdf->Cell(130, 6, $langs->trans('MahnungGebuehr'), 0, 0, 'L'); + $pdf->Cell(130, 6, $outputlangs->trans('MahnungGebuehr'), 0, 0, 'L'); $pdf->Cell(30, 6, price((float) $mahnung->mahngebuehr).' EUR', 0, 1, 'R'); } if ((float) $mahnung->pauschale_b2b > 0) { $pdf->SetX(25); - $pdf->Cell(130, 6, $langs->trans('MahnungPauschaleB2B').' (BGB §288 Abs. 5)', 0, 0, 'L'); + $pdf->Cell(130, 6, $outputlangs->trans('MahnungPauschaleB2B').' (BGB §288 Abs. 5)', 0, 0, 'L'); $pdf->Cell(30, 6, price((float) $mahnung->pauschale_b2b).' EUR', 0, 1, 'R'); } if ((float) $mahnung->verzugszinsen > 0) { @@ -239,7 +229,7 @@ class MahnungPdf ? (float) getDolGlobalString('MAHNUNG_AUFSCHLAG_B2B', '9.0') : (float) getDolGlobalString('MAHNUNG_AUFSCHLAG_B2C', '5.0'); $satz = $basis + $auf; - $pdf->Cell(130, 6, $langs->trans('MahnungVerzugszinsen').' ('.number_format($satz, 2, ',', '.').' %)', 0, 0, 'L'); + $pdf->Cell(130, 6, $outputlangs->trans('MahnungVerzugszinsen').' ('.number_format($satz, 2, ',', '.').' %)', 0, 0, 'L'); $pdf->Cell(30, 6, price((float) $mahnung->verzugszinsen).' EUR', 0, 1, 'R'); } @@ -247,7 +237,7 @@ class MahnungPdf $pdf->Ln(1); $pdf->SetX(25); $pdf->SetFont('helvetica', 'B', 11); - $pdf->Cell(130, 7, $langs->trans('MahnungSumme'), 'T', 0, 'L'); + $pdf->Cell(130, 7, $outputlangs->trans('MahnungSumme'), 'T', 0, 'L'); $pdf->Cell(30, 7, price((float) $mahnung->summe_mahnung).' EUR', 'T', 1, 'R'); $pdf->Ln(5); @@ -270,9 +260,6 @@ class MahnungPdf /** * Fusszeile mit Bankverbindung + Firmen-Footer. - * - * @param TCPDF $pdf - * @return void */ private function renderFooter($pdf) { @@ -288,10 +275,9 @@ class MahnungPdf if (!empty($mysoc->phone)) { $lines[] = 'Tel: '.$mysoc->phone; } - // Bankverbindung aus Standard-Bankaccount - $bankAccount = $this->getDefaultBankLine(); - if (!empty($bankAccount)) { - $lines[] = $bankAccount; + $bankLine = $this->getDefaultBankLine(); + if (!empty($bankLine)) { + $lines[] = $bankLine; } foreach ($lines as $l) { $pdf->Cell(0, 4, $l, 0, 1, 'C'); @@ -299,7 +285,7 @@ class MahnungPdf } /** - * Standard-Bankkonto in einer Zeile (Bank · IBAN · BIC). + * Standard-Bankkonto in einer Zeile. * * @return string */ @@ -319,7 +305,7 @@ class MahnungPdf } /** - * Default-Intro je Stufe (wenn Setup leer ist). + * Default-Intro je Stufe. * * @param int $stufe * @return string @@ -344,12 +330,13 @@ class MahnungPdf } /** - * Ziel-Verzeichnis: documents/facture/{ref}/ + * Ziel-Verzeichnis: documents/facture/{ref}/ (damit die Mahnung im Rechnung-Tab erscheint). * + * @param Mahnung $mahnung * @param Facture $facture * @return string */ - private function getOutputDir($facture) + private function getOutputDir($mahnung, $facture) { global $conf; $documentDir = !empty($conf->facture->multidir_output[$facture->entity]) diff --git a/core/modules/mahnung/modules_mahnung.php b/core/modules/mahnung/modules_mahnung.php new file mode 100644 index 0000000..30722e7 --- /dev/null +++ b/core/modules/mahnung/modules_mahnung.php @@ -0,0 +1,55 @@ + + * + * 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/core/modules/mahnung/modules_mahnung.php + * \ingroup mahnung + * \brief Abstrakte Basis-Klasse fuer Mahnung-Dokumentengeneratoren. + */ + +require_once DOL_DOCUMENT_ROOT.'/core/class/commondocgenerator.class.php'; + +/** + * Elternklasse der Mahnung-Dokumentengeneratoren (PDF, ODT). + */ +abstract class ModelePDFMahnung extends CommonDocGenerator +{ + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Gibt die Liste der aktiven Dokumentenmodelle zurueck. + * + * @param DoliDB $db Datenbank-Handler + * @param int $maxfilenamelength Max Laenge + * @return array|int + */ + public static function liste_modeles($db, $maxfilenamelength = 0) + { + // phpcs:enable + $type = 'mahnung'; + $list = array(); + + include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; + $list = getListOfModels($db, $type, $maxfilenamelength); + + return $list; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Erzeugt das Dokument auf der Festplatte. + * + * @param object $object Mahnung-Objekt + * @param Translate $outputlangs Ausgabesprache + * @param string $srctemplatepath Pfad zum Template (ODT) + * @param int $hidedetails Details ausblenden + * @param int $hidedesc Beschreibung ausblenden + * @param int $hideref Referenz ausblenden + * @return int 1=OK, <=0=Fehler + */ + abstract public function write_file($object, $outputlangs, $srctemplatepath = '', $hidedetails = 0, $hidedesc = 0, $hideref = 0); + // phpcs:enable +} diff --git a/core/modules/modMahnung.class.php b/core/modules/modMahnung.class.php index 3557b8d..6da907f 100644 --- a/core/modules/modMahnung.class.php +++ b/core/modules/modMahnung.class.php @@ -69,7 +69,7 @@ class modMahnung extends DolibarrModules 'menus' => 0, 'tpl' => 0, 'barcode' => 0, - 'models' => 0, + 'models' => 1, 'printing' => 0, 'theme' => 0, 'css' => array(), @@ -154,6 +154,24 @@ class modMahnung extends DolibarrModules 'allentities', 1, ), + 5 => array( + 'MAHNUNG_ADDON_PDF', + 'chaine', + 'standard_mahnung', + 'Standard-Dokumentenmodell fuer Mahnungen', + 0, + 'current', + 1, + ), + 6 => array( + 'MAHNUNG_ADDON_PDF_ODT_PATH', + 'chaine', + 'DOL_DATA_ROOT/doctemplates/mahnung', + 'Verzeichnis fuer ODT-Templates', + 0, + 'current', + 1, + ), ); if (!isModEnabled('mahnung')) { @@ -166,7 +184,12 @@ class modMahnung extends DolibarrModules $this->dictionaries = array(); - $this->boxes = array(); + $this->boxes = array( + 0 => array( + 'file' => 'box_mahnung_offen@mahnung', + 'enabledbydefaulton' => 'Home', + ), + ); // Cron-Job: Vorschlagsliste taeglich 06:00 $this->cronjobs = array( @@ -281,13 +304,25 @@ class modMahnung extends DolibarrModules */ public function init($options = '') { + global $conf; + // Tabellen anlegen aus sql/-Verzeichnis $result = $this->_load_tables('/mahnung/sql/'); if ($result < 0) { return -1; } + // Dokumentenmodelle registrieren $sql = array(); + $sql[] = "DELETE FROM ".MAIN_DB_PREFIX."document_model WHERE nom = 'standard_mahnung' AND type = 'mahnung' AND entity = ".((int) $conf->entity); + $sql[] = "INSERT INTO ".MAIN_DB_PREFIX."document_model (nom, type, entity, libelle, description) VALUES ('standard_mahnung', 'mahnung', ".((int) $conf->entity).", 'Standard PDF (DIN 5008)', NULL)"; + $sql[] = "DELETE FROM ".MAIN_DB_PREFIX."document_model WHERE nom = 'generic_mahnung_odt' AND type = 'mahnung' AND entity = ".((int) $conf->entity); + $sql[] = "INSERT INTO ".MAIN_DB_PREFIX."document_model (nom, type, entity, libelle, description) VALUES ('generic_mahnung_odt', 'mahnung', ".((int) $conf->entity).", 'ODT templates', 'MAHNUNG_ADDON_PDF_ODT_PATH')"; + + // ODT-Template-Verzeichnis anlegen + $doctemplatedir = DOL_DATA_ROOT.'/doctemplates/mahnung'; + dol_mkdir($doctemplatedir); + return $this->_init($sql, $options); } diff --git a/langs/de_DE/mahnung.lang b/langs/de_DE/mahnung.lang index be23383..bdfc45a 100644 --- a/langs/de_DE/mahnung.lang +++ b/langs/de_DE/mahnung.lang @@ -109,3 +109,16 @@ MahnungSettingsSaved = Einstellungen gespeichert. # MahnungCronBuildVorschlag = Mahnwesen — Vorschlagsliste aufbauen MahnungCronBuildVorschlagDesc = Sucht taeglich ueberfaellige Rechnungen und sendet einen Ntfy-Push mit der Anzahl neuer Vorschlaege. + +# +# Widget +# +MahnungBoxOffeneRechnungen = Ueberfaellige Rechnungen mit Mahnstufe (%s) + +# +# Dokumentenmodelle +# +MahnungDokumentModelle = Dokumentenmodelle +MahnungPdfStandard = Standard-PDF (DIN 5008) +MahnungGenerate = Dokument generieren +NoDocuments = Keine Dokumente vorhanden.