Преглед изворни кода

Add reusable order PDF rendering and admin print action.

Refactor PDF generation for mail attachments and a new admin endpoint,
update the pick-list layout, and document the flow in MAIL_PROCESS.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
Medowar пре 6 дана
родитељ
комит
ff531fa922
4 измењених фајлова са 134 додато и 31 уклоњено
  1. 30 0
      admin/order-pdf.php
  2. 8 0
      admin/order.php
  3. 2 0
      docs/MAIL_PROCESS.md
  4. 94 31
      includes/functions.php

+ 30 - 0
admin/order-pdf.php

@@ -0,0 +1,30 @@
+<?php
+require_once __DIR__ . "/../config.php";
+require_once __DIR__ . "/../includes/functions.php";
+
+if (empty($_SESSION['admin_logged_in'])) {
+    header("Location: login.php");
+    exit();
+}
+
+$orderId = trim((string) ($_GET['id'] ?? ""));
+if ($orderId === "") {
+    http_response_code(400);
+    header("Content-Type: text/plain; charset=UTF-8");
+    echo "Keine Bestellnummer angegeben.";
+    exit();
+}
+
+$order = getOrderById($orderId);
+if ($order === null) {
+    http_response_code(404);
+    header("Content-Type: text/plain; charset=UTF-8");
+    echo "Bestellung nicht gefunden.";
+    exit();
+}
+
+streamOrderPdf(
+    $order,
+    "bestellung-" . strtolower($order["id"]) . ".pdf",
+    true,
+);

+ 8 - 0
admin/order.php

@@ -131,6 +131,14 @@ include __DIR__ . "/../includes/header.php";
         ? "Bestellung " . escape($order["id"])
         : "Bestellung"; ?></h2>
     <div class="admin-dashboard-actions">
+        <?php if ($order !== null): ?>
+            <a
+                href="order-pdf.php?id=<?php echo urlencode($order["id"]); ?>"
+                class="btn btn-secondary"
+                target="_blank"
+                rel="noopener noreferrer"
+            >Bestellung drucken</a>
+        <?php endif; ?>
         <a href="index.php" class="btn btn-secondary">Zurück zum Dashboard</a>
         <a href="orders.php" class="btn">Zurück zur Bestellliste</a>
     </div>

+ 2 - 0
docs/MAIL_PROCESS.md

@@ -58,6 +58,8 @@ Die folgenden Parameter steuern den Mailfluss:
 - Empfänger: `getOrderRecipientEmail()` (normalisiert/validiert)
 - Inhalt: HTML-Bestellzusammenfassung
 - Optional: PDF-Anhang `bestellung-<order-id>.pdf` bei aktivem `attach_order_pdf_to_admin_email`
+- PDF-Erzeugung: `renderOrderPdf($order)` (intern `prepareOrderForDocument()` + `generateOrderPdf()`); enthält keine Bearbeitungs-/Lieferstatus-Felder aus der Admin-Oberfläche
+- Admin-Nachdruck: `admin/order-pdf.php?id=<order-id>` (Schaltfläche „Bestellung drucken“ auf `admin/order.php`)
 
 ## Ablauf nach Konfiguration
 

+ 94 - 31
includes/functions.php

@@ -2697,7 +2697,7 @@ function sendConfirmedOrderAdminNotification($order)
         $attachments[] = [
             "filename" => "bestellung-" . strtolower($order["id"]) . ".pdf",
             "content_type" => "application/pdf",
-            "content" => generateOrderPdf($order),
+            "content" => renderOrderPdf($order),
         ];
     }
 
@@ -2873,6 +2873,63 @@ function pdfWrapAnsiText($text, $maxChars)
     return $lines;
 }
 
+function prepareOrderForDocument(array $order): array
+{
+    $items = [];
+    foreach ($order["items"] ?? [] as $item) {
+        if (!is_array($item)) {
+            continue;
+        }
+
+        $items[] = [
+            "product_name" => trim((string) ($item["product_name"] ?? "")),
+            "size" => trim((string) ($item["size"] ?? "")),
+            "availability_label" => trim(
+                (string) ($item["availability_label"] ?? ""),
+            ),
+        ];
+    }
+
+    return [
+        "id" => trim((string) ($order["id"] ?? "")),
+        "created_at" => trim((string) ($order["created_at"] ?? "")),
+        "customer_name" => trim((string) ($order["customer_name"] ?? "")),
+        "customer_email" => trim((string) ($order["customer_email"] ?? "")),
+        "organization_label" => trim(
+            (string) ($order["organization_label"] ?? ""),
+        ),
+        "comment" => (string) ($order["comment"] ?? ""),
+        "items" => $items,
+    ];
+}
+
+function renderOrderPdf(array $order): string
+{
+    return generateOrderPdf(prepareOrderForDocument($order));
+}
+
+function streamOrderPdf(
+    array $order,
+    string $filename,
+    bool $inline = true,
+): void {
+    $pdf = renderOrderPdf($order);
+    $safeFilename = preg_replace('/[^A-Za-z0-9._-]+/', "-", $filename) ?: "bestellung.pdf";
+    $disposition = $inline ? "inline" : "attachment";
+
+    header("Content-Type: application/pdf");
+    header(
+        'Content-Disposition: ' .
+            $disposition .
+            '; filename="' .
+            $safeFilename .
+            '"',
+    );
+    header("Content-Length: " . strlen($pdf));
+    echo $pdf;
+    exit();
+}
+
 function generateOrderPdf($order)
 {
     $pageWidth = 595;
@@ -2881,6 +2938,7 @@ function generateOrderPdf($order)
     $topY = 800;
     $bottomY = 60;
     $lineHeight = 14;
+    $itemLineHeight = 16;
 
     $pages = [];
     $pageContent = "";
@@ -2891,7 +2949,6 @@ function generateOrderPdf($order)
         return pdfEncodeWinAnsi((string) $text);
     };
 
-    $siteName = $encodeText(SITE_FULL_NAME);
     $orderId = $encodeText($order["id"]);
     $createdAt = $encodeText(formatDate($order["created_at"]));
     $customerName = $encodeText($order["customer_name"]);
@@ -2920,7 +2977,6 @@ function generateOrderPdf($order)
         $topY,
         $leftMargin,
         $lineHeight,
-        $siteName,
         $orderId,
         $createdAt,
         $writeText,
@@ -2934,17 +2990,12 @@ function generateOrderPdf($order)
         $pageContent = "";
         $y = $topY;
 
-        foreach (pdfWrapAnsiText($siteName, 70) as $index => $line) {
-            $writeText($leftMargin, $y, $line, $index === 0 ? 15 : 11);
-            $y -= $index === 0 ? 19 : $lineHeight;
-        }
-
         $headerLine = "Bestellung: " . $orderId;
         if ($pageNumber > 1) {
-            $headerLine .= " | Seite " . $encodeText($pageNumber);
+            $headerLine .= " | Seite " . $encodeText((string) $pageNumber);
         }
-        $writeText($leftMargin, $y, $headerLine, 12);
-        $y -= 17;
+        $writeText($leftMargin, $y, $headerLine, 14);
+        $y -= 19;
         $writeText($leftMargin, $y, "Erstellt am: " . $createdAt, 11);
         $y -= 20;
     };
@@ -2959,18 +3010,20 @@ function generateOrderPdf($order)
         $encodedText,
         $maxChars,
         $fontSize = 11,
-        $x = null
+        $x = null,
+        $spacing = null
     ) use (&$y, $lineHeight, $leftMargin, $writeText, $ensureSpace) {
         $targetX = $x === null ? $leftMargin : $x;
+        $step = $spacing === null ? $lineHeight : $spacing;
         $lines = pdfWrapAnsiText($encodedText, $maxChars);
         foreach ($lines as $line) {
-            $ensureSpace($lineHeight);
+            $ensureSpace($step);
             $writeText($targetX, $y, $line, $fontSize);
-            $y -= $lineHeight;
+            $y -= $step;
         }
     };
 
-    $writeSectionTitle = function ($titleText) use (
+    $writeSectionTitle = function ($titleText, $fontSize = 13) use (
         &$y,
         $leftMargin,
         $writeText,
@@ -2978,7 +3031,7 @@ function generateOrderPdf($order)
         $encodeText
     ) {
         $ensureSpace(24);
-        $writeText($leftMargin, $y, $encodeText($titleText), 13);
+        $writeText($leftMargin, $y, $encodeText($titleText), $fontSize);
         $y -= 18;
     };
 
@@ -2990,11 +3043,11 @@ function generateOrderPdf($order)
     $writeWrapped("E-Mail: " . $customerEmail, 80);
     $y -= 6;
 
-    $writeSectionTitle("Artikelliste");
+    $writeSectionTitle("Artikelliste", 15);
 
     $itemNumber = 1;
     if (empty($order["items"])) {
-        $writeWrapped($encodeText("Keine Artikel"), 80);
+        $writeWrapped($encodeText("Keine Artikel"), 80, 12);
     } else {
         foreach ($order["items"] as $item) {
             $itemName = $encodeText($item["product_name"]);
@@ -3003,17 +3056,35 @@ function generateOrderPdf($order)
                 preg_replace("/\s+/", " ", (string) $item["availability_label"]),
             );
 
-            $writeWrapped($encodeText($itemNumber . ". ") . $itemName, 78);
+            $writeWrapped(
+                $encodeText($itemNumber . ". ") . $itemName,
+                68,
+                14,
+                null,
+                $itemLineHeight,
+            );
 
             if ($sizeLabel !== "") {
-                $writeWrapped($encodeText("   Größe: ") . $sizeLabel, 76);
+                $writeWrapped(
+                    $encodeText("   Größe: ") . $sizeLabel,
+                    66,
+                    12,
+                    null,
+                    $itemLineHeight,
+                );
             }
 
             if ($hintLabel !== "") {
-                $writeWrapped($encodeText("   Hinweis: ") . $hintLabel, 76);
+                $writeWrapped(
+                    $encodeText("   Hinweis: ") . $hintLabel,
+                    66,
+                    12,
+                    null,
+                    $itemLineHeight,
+                );
             }
 
-            $y -= 4;
+            $y -= 8;
             $itemNumber++;
         }
     }
@@ -3033,20 +3104,12 @@ function generateOrderPdf($order)
     $writeSectionTitle("Lagerbearbeitung");
 
     $warehouseLines = [
-        "Ausgegeben am: ________________________",
-        "Ausgegeben durch: _____________________",
-        "Unterschrift: _________________________",
-        "",
+        "Ausgegeben am / durch: __________________________________________________",
         "[ ] Vollständig ausgegeben",
         "[ ] Teilweise ausgegeben",
     ];
 
     foreach ($warehouseLines as $line) {
-        if ($line === "") {
-            $ensureSpace($lineHeight);
-            $y -= $lineHeight;
-            continue;
-        }
         $writeWrapped($encodeText($line), 80);
     }