bericht/bericht_batch.php
Eduard Wisch d40587845f
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
PDF-Header mit Logo+Titel, Footer mit Seitenzahl, Hack-Font beschreibbar [deploy]
- Neue Klasse BerichtPdf / BerichtPdfFpdi (Trait-basiert):
  * Header: links Bericht-Titel (Bold) + Firmenname, rechts Firmen-Logo (max 40x18mm),
    Trennlinie. Top-Margin jetzt 30mm für den Header-Bereich.
  * Footer: zentriert "Seite X / Y" mit TCPDF-Aliases.
  * berichtInit(): kompiliert Hack-TTFs nach DOL_DATA_ROOT/bericht/tcpdf_fonts/
    (beschreibbar) und bindet sie per AddFont an die PDF-Instance.
    Vorher schlug addTTFfont still fehl weil K_PATH_FONTS read-only war —
    deshalb kam weder Titel noch Notiz in Hack.
- bericht_ensure_hack_font($pdf) zieht den Font-Key jetzt aus der Instance
  (BerichtPdfTrait), sonst Fallback helvetica.
- bericht_write_note_html() wrapped das CKEditor-HTML in
  <span style="font-family:hack...;"> damit writeHTMLCell den Hack-Font
  tatsächlich verwendet.
- Composite-Branch: $mT=30 / $mB=16 damit Bilder nicht unter dem Header
  sitzen.
- ajax/generate_pdf, ajax/preview_pdf, api/pdf, api/reports, bericht_batch:
  alle nutzen jetzt BerichtPdf(Fpdi), setzen SetMargins(10,30,10),
  setPrintHeader(true) und berichtInit() mit Titel, mysoc->name und Logo.
2026-04-09 15:39:42 +02:00

185 lines
8.1 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/* Batch-Modus: mehrere Berichte zu einem Sammel-PDF zusammenführen.
* Nur finale Berichte mit existierendem final_pdf_path werden berücksichtigt.
*
* GET: filter per Datum-Von/Bis, Element-Typ, Kundensuche, Status
* POST: action=generate + ids[] → liefert ein zusammengesetztes PDF aus den ausgewählten
*/
$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 && file_exists("../../main.inc.php")) $res = @include "../../main.inc.php";
if (!$res) die("Include of main fails");
require_once __DIR__.'/class/bericht.class.php';
require_once __DIR__.'/lib/bericht.lib.php';
if (!$user->hasRight('bericht', 'read')) accessforbidden();
$langs->loadLangs(array("bericht@bericht", "main"));
$action = GETPOST('action', 'alpha');
$datefrom = GETPOST('datefrom', 'alpha');
$dateto = GETPOST('dateto', 'alpha');
$q = GETPOST('q', 'alpha');
/* ---------- Batch-PDF generieren ---------- */
if ($action === 'generate') {
$ids = GETPOST('ids', 'array');
if (empty($ids)) {
setEventMessages('Keine Berichte ausgewählt', null, 'errors');
header("Location: ".$_SERVER['PHP_SELF']); exit;
}
// TCPDF + FPDI laden
$tcpdf_loaded = false;
foreach (array(
DOL_DOCUMENT_ROOT.'/includes/tecnickcom/tcpdf/tcpdf.php',
DOL_DOCUMENT_ROOT.'/includes/tcpdf/tcpdf.php',
) as $p) { if (file_exists($p)) { require_once $p; $tcpdf_loaded = true; break; } }
if (!$tcpdf_loaded) { http_response_code(500); exit('TCPDF fehlt'); }
$fpdi_loaded = false;
foreach (array(
DOL_DOCUMENT_ROOT.'/includes/setasign/vendor/setasign/fpdi/src/Tcpdf/Fpdi.php',
DOL_DOCUMENT_ROOT.'/includes/fpdi/src/Tcpdf/Fpdi.php',
) as $p) { if (file_exists($p)) { require_once $p; $fpdi_loaded = true; break; } }
if (!$fpdi_loaded) { http_response_code(500); exit('FPDI wird für Batch-Modus benötigt'); }
require_once __DIR__.'/class/berichtpdf.class.php';
$pdf = class_exists('BerichtPdfFpdi')
? new BerichtPdfFpdi('P', 'mm', 'A4', true, 'UTF-8', false)
: new \setasign\Fpdi\Tcpdf\Fpdi('P', 'mm', 'A4', true, 'UTF-8', false);
global $mysoc;
$pdf->SetCreator('Dolibarr Bericht-Modul Batch');
$pdf->SetAuthor($user->getFullName($langs));
$pdf->SetTitle('Bericht-Sammlung '.dol_print_date(dol_now(), '%Y-%m-%d'));
$logo_path = !empty($mysoc->logo) ? $conf->mycompany->dir_output.'/logos/'.$mysoc->logo : '';
if (method_exists($pdf, 'berichtInit')) {
$pdf->berichtInit('Bericht-Sammlung', $mysoc->name ?? '', $logo_path);
}
$pdf->SetMargins(10, 30, 10);
$pdf->SetAutoPageBreak(true, 16);
$pdf->setPrintHeader(true);
$pdf->setPrintFooter(true);
$pdf->setHeaderMargin(5);
$pdf->setFooterMargin(10);
// Inhaltsverzeichnis-Seite
$pdf->AddPage('P', 'A4');
$pdf->SetFont(bericht_ensure_hack_font($pdf), 'B', 18);
$pdf->Cell(0, 12, 'Bericht-Sammlung', 0, 1, 'C');
$pdf->SetFont(bericht_ensure_hack_font($pdf), '', 11);
$pdf->Cell(0, 8, 'Erstellt: '.dol_print_date(dol_now(), 'dayhour'), 0, 1, 'C');
$pdf->Ln(8);
$pdf->SetFont(bericht_ensure_hack_font($pdf), 'B', 13);
$pdf->Cell(0, 8, 'Enthaltene Berichte ('.count($ids).')', 0, 1, 'L');
$pdf->SetFont(bericht_ensure_hack_font($pdf), '', 10);
$berichte = array();
foreach ($ids as $id) {
$b = new Bericht($db);
if ($b->fetch((int) $id) > 0 && $b->status == Bericht::STATUS_FINAL && !empty($b->final_pdf_path)) {
$pp = bericht_resolve_data_path($b->final_pdf_path);
if ($pp && file_exists($pp)) {
$berichte[] = array('bericht' => $b, 'pdf' => $pp);
$pdf->Cell(0, 6, '• '.$b->ref.' '.($b->titel ?: '').' ('.$b->auftragsnummer.')', 0, 1, 'L');
}
}
}
if (empty($berichte)) {
exit('Keine gültigen (finalisierten) Berichte in der Auswahl');
}
// Jede Bericht-PDF als volle Seiten einbauen
foreach ($berichte as $entry) {
try {
$page_count = $pdf->setSourceFile($entry['pdf']);
for ($n = 1; $n <= $page_count; $n++) {
$tpl = $pdf->importPage($n);
$size = $pdf->getTemplateSize($tpl);
$pdf->AddPage($size['orientation'] ?? 'P', array($size['width'], $size['height']));
$pdf->useTemplate($tpl);
}
} catch (Throwable $e) { continue; }
}
$filename = 'Bericht-Sammlung_'.dol_print_date(dol_now(), '%Y%m%d_%H%M%S').'.pdf';
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="'.$filename.'"');
$pdf->Output($filename, 'D');
exit;
}
/* ---------- Listen-Ansicht ---------- */
llxHeader('', 'Bericht-Batch-Modus');
print load_fiche_titre('📦 Bericht-Batch — Mehrere Berichte zu einem PDF', '', 'bill');
// Filter
print '<form method="get" class="bericht-batch-filter" style="margin-bottom:16px;padding:12px;background:var(--colorbacktitle1,#f5f5f5);border-radius:6px;">';
print '<label>Von: <input type="date" name="datefrom" value="'.dol_escape_htmltag($datefrom).'"></label> &nbsp; ';
print '<label>Bis: <input type="date" name="dateto" value="'.dol_escape_htmltag($dateto).'"></label> &nbsp; ';
print '<label>Suche: <input type="text" name="q" value="'.dol_escape_htmltag($q).'" placeholder="Ref, Titel, Auftragsnr"></label> &nbsp; ';
print '<button type="submit" class="butAction">🔍 Filtern</button>';
print '</form>';
// Query: nur finale Berichte mit PDF, gefiltert
$where = "status = 1 AND final_pdf_path IS NOT NULL AND COALESCE(is_template,0) = 0 AND entity IN (".getEntity('bericht').")";
if ($datefrom) {
$where .= " AND datec >= '".$db->escape($datefrom." 00:00:00")."'";
}
if ($dateto) {
$where .= " AND datec <= '".$db->escape($dateto." 23:59:59")."'";
}
if ($q) {
$qe = $db->escape($q);
$where .= " AND (ref LIKE '%$qe%' OR titel LIKE '%$qe%' OR auftragsnummer LIKE '%$qe%')";
}
$sql = "SELECT rowid, ref, titel, auftragsnummer, element_type, fk_element, datec FROM ".$db->prefix()."bericht WHERE ".$where." ORDER BY datec DESC LIMIT 500";
$resq = $db->query($sql);
print '<form method="post" action="'.$_SERVER['PHP_SELF'].'">';
print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="action" value="generate">';
print '<table class="noborder centpercent">';
print '<tr class="liste_titre">';
print '<th><input type="checkbox" onclick="document.querySelectorAll(\'.bt-check\').forEach(c => c.checked = this.checked)"></th>';
print '<th>Ref</th>';
print '<th>Titel</th>';
print '<th>Auftragsnummer</th>';
print '<th>Datum</th>';
print '</tr>';
$count = 0;
if ($resq) {
while ($o = $db->fetch_object($resq)) {
print '<tr class="oddeven">';
print '<td><input type="checkbox" class="bt-check" name="ids[]" value="'.$o->rowid.'"></td>';
print '<td>'.dol_escape_htmltag($o->ref).'</td>';
print '<td>'.dol_escape_htmltag($o->titel).'</td>';
print '<td>'.dol_escape_htmltag($o->auftragsnummer).'</td>';
print '<td>'.dol_print_date($db->jdate($o->datec), 'dayhour').'</td>';
print '</tr>';
$count++;
}
}
print '</table>';
if ($count === 0) {
print '<p class="opacitymedium">Keine finalisierten Berichte gefunden.</p>';
} else {
print '<div style="margin-top:16px;text-align:right;">';
print '<button type="submit" class="butActionConfirm">📦 Ausgewählte als Sammel-PDF herunterladen</button>';
print '</div>';
}
print '</form>';
llxFooter();
$db->close();