feat(11-receipt-print): phase 11 complete — receipt preview, print & PDF

Add receipt show/print/pdf endpoints with dompdf integration.
Active preview and PDF links in order Documents tab.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-15 20:31:04 +01:00
parent ed057fc304
commit fb60b6d5d7
14 changed files with 815 additions and 2493 deletions

View File

@@ -260,4 +260,95 @@ final class ReceiptController
return null;
}
public function show(Request $request): Response
{
$orderId = max(0, (int) $request->input('id', 0));
$receiptId = max(0, (int) $request->input('receiptId', 0));
$receipt = $this->receipts->findById($receiptId);
if ($receipt === null || (int) ($receipt['order_id'] ?? 0) !== $orderId) {
return Response::html('Not found', 404);
}
$data = $this->buildReceiptViewData($receipt, $orderId);
$html = $this->template->render('receipts/show', $data, 'layouts/app');
return Response::html($html);
}
public function printView(Request $request): Response
{
$orderId = max(0, (int) $request->input('id', 0));
$receiptId = max(0, (int) $request->input('receiptId', 0));
$receipt = $this->receipts->findById($receiptId);
if ($receipt === null || (int) ($receipt['order_id'] ?? 0) !== $orderId) {
return Response::html('Not found', 404);
}
$data = $this->buildReceiptViewData($receipt, $orderId);
$html = $this->template->render('receipts/print', $data);
return Response::html($html);
}
public function pdf(Request $request): Response
{
$orderId = max(0, (int) $request->input('id', 0));
$receiptId = max(0, (int) $request->input('receiptId', 0));
$receipt = $this->receipts->findById($receiptId);
if ($receipt === null || (int) ($receipt['order_id'] ?? 0) !== $orderId) {
return Response::html('Not found', 404);
}
$data = $this->buildReceiptViewData($receipt, $orderId);
$html = $this->template->render('receipts/print', $data);
$dompdf = new \Dompdf\Dompdf();
$dompdf->loadHtml($html);
$dompdf->setPaper('A4');
$dompdf->render();
$filename = str_replace(['/', '\\'], '_', (string) ($receipt['receipt_number'] ?? 'receipt')) . '.pdf';
return new Response($dompdf->output() ?: '', 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
]);
}
/**
* @param array<string, mixed> $receipt
* @return array<string, mixed>
*/
private function buildReceiptViewData(array $receipt, int $orderId): array
{
$seller = json_decode((string) ($receipt['seller_data_json'] ?? '{}'), true);
$buyer = ($receipt['buyer_data_json'] ?? null) !== null
? json_decode((string) $receipt['buyer_data_json'], true)
: null;
$items = json_decode((string) ($receipt['items_json'] ?? '[]'), true);
$configName = '';
$config = $this->receiptConfigs->findById((int) ($receipt['config_id'] ?? 0));
if ($config !== null) {
$configName = (string) ($config['name'] ?? '');
}
return [
'title' => $this->translator->get('receipts.show.title') . ' ' . ($receipt['receipt_number'] ?? ''),
'activeMenu' => 'orders',
'activeOrders' => 'list',
'user' => $this->auth->user(),
'orderId' => $orderId,
'receipt' => $receipt,
'seller' => is_array($seller) ? $seller : [],
'buyer' => is_array($buyer) ? $buyer : null,
'items' => is_array($items) ? $items : [],
'configName' => $configName,
];
}
}