diff --git a/admin/setup.php b/admin/setup.php
index da17ad5..373d94a 100644
--- a/admin/setup.php
+++ b/admin/setup.php
@@ -105,6 +105,14 @@ print '
';
print '
🔌 REST-API Status
';
diff --git a/ajax/create_grid_page.php b/ajax/create_grid_page.php
index 5a8cf1c..d2fb63f 100644
--- a/ajax/create_grid_page.php
+++ b/ajax/create_grid_page.php
@@ -14,7 +14,7 @@ $relpaths_raw = (string) ($_POST['relpaths'] ?? '[]');
$relpaths = json_decode($relpaths_raw, true);
if (!is_array($relpaths) || empty($relpaths)) bericht_ajax_fail('Keine Bilder ausgewählt');
-$valid_layouts = array('grid_2', 'grid_2v', 'grid_4', 'grid_6');
+$valid_layouts = array('grid_2', 'grid_2v', 'grid_4', 'grid_6', 'before_after');
if (!in_array($layout, $valid_layouts, true)) $layout = 'grid_4';
$bericht = new Bericht($db);
diff --git a/api/pages.php b/api/pages.php
index 29ae2c0..9ff7c60 100644
--- a/api/pages.php
+++ b/api/pages.php
@@ -213,7 +213,7 @@ if ($method === 'DELETE' || ($method === 'POST' && ($_GET['delete'] ?? '') === '
api_ok();
}
-/* ---------- UPDATE page (note, rotation) ---------- */
+/* ---------- UPDATE page (note, title, rotation, layout) ---------- */
if ($method === 'POST') {
$in = api_input();
$sets = array();
@@ -221,11 +221,21 @@ if ($method === 'POST') {
$note = (string) $in['note'];
$sets[] = "note = ".($note !== '' ? "'".$db->escape($note)."'" : "NULL");
}
+ if (isset($in['title'])) {
+ $title = (string) $in['title'];
+ $sets[] = "title = ".($title !== '' ? "'".$db->escape($title)."'" : "NULL");
+ }
if (isset($in['rotation'])) {
$rot = (int) $in['rotation'];
$rot = (($rot % 360) + 360) % 360;
$sets[] = "rotation = ".$rot;
}
+ if (isset($in['layout'])) {
+ $valid = array('single','grid_2','grid_2v','grid_4','grid_6','before_after','title_only');
+ if (in_array($in['layout'], $valid, true)) {
+ $sets[] = "layout = '".$db->escape($in['layout'])."'";
+ }
+ }
if (empty($sets)) api_fail('Nichts zu aktualisieren');
$sql = "UPDATE ".$db->prefix()."bericht_page SET ".implode(',', $sets)." WHERE rowid = ".$page_id;
diff --git a/bericht_batch.php b/bericht_batch.php
new file mode 100644
index 0000000..07e2f6e
--- /dev/null
+++ b/bericht_batch.php
@@ -0,0 +1,173 @@
+ 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'); }
+
+ $pdf = new \setasign\Fpdi\Tcpdf\Fpdi('P', 'mm', 'A4', true, 'UTF-8', false);
+ $pdf->SetCreator('Dolibarr Bericht-Modul Batch');
+ $pdf->SetAuthor($user->getFullName($langs));
+ $pdf->SetTitle('Bericht-Sammlung '.dol_print_date(dol_now(), '%Y-%m-%d'));
+ $pdf->setPrintHeader(false);
+ $pdf->setPrintFooter(false);
+
+ // Inhaltsverzeichnis-Seite
+ $pdf->AddPage('P', 'A4');
+ $pdf->SetFont('helvetica', 'B', 18);
+ $pdf->Cell(0, 12, 'Bericht-Sammlung', 0, 1, 'C');
+ $pdf->SetFont('helvetica', '', 11);
+ $pdf->Cell(0, 8, 'Erstellt: '.dol_print_date(dol_now(), 'dayhour'), 0, 1, 'C');
+ $pdf->Ln(8);
+ $pdf->SetFont('helvetica', 'B', 13);
+ $pdf->Cell(0, 8, 'Enthaltene Berichte ('.count($ids).')', 0, 1, 'L');
+ $pdf->SetFont('helvetica', '', 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 '
';
+
+// 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 '
';
+
+llxFooter();
+$db->close();
diff --git a/bericht_card.php b/bericht_card.php
index d0d3da7..33fb827 100644
--- a/bericht_card.php
+++ b/bericht_card.php
@@ -103,6 +103,17 @@ if ($action === 'create' && $user->hasRight('bericht', 'write')) {
setEventMessages($b->error, $b->errors, 'errors');
}
+// Aktion: neue Version erstellen
+if ($action === 'new_version' && $berichtid > 0 && $user->hasRight('bericht', 'write')) {
+ $new_id = $bericht->duplicateAsNewVersion($user);
+ if ($new_id) {
+ setEventMessages('Neue Version erstellt', null, 'mesgs');
+ header("Location: ".$_SERVER['PHP_SELF'].'?berichtid='.$new_id);
+ exit;
+ }
+ setEventMessages('Versionierung fehlgeschlagen', null, 'errors');
+}
+
// Aktion: Bericht löschen
if ($action === 'delete' && $berichtid > 0 && $user->hasRight('bericht', 'delete')) {
if ($bericht->delete($user) > 0) {
@@ -310,8 +321,24 @@ if (!$bericht) {
print '