Files
Additional/app/Services/QrStickerService.php
T
twotalesanimation 09198a9f7d fix: Generate A6 sticker PDF with inline HTML instead of blade template
Build HTML directly in service without relying on view files. This eliminates
dependency on blade template file existing on production server.

Include all styling inline in heredoc string with:
- A6 dimensions (148mm  105mm)
- Order number, customer surname, design name
- Order status and date
- QR code in SVG format
- Clean, printable layout

Add helper methods:
- truncate(): Truncate design name to fit sticker
- formatStatus(): Format order status for display
2026-01-02 20:28:44 +02:00

309 lines
8.8 KiB
PHP
Raw 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
namespace App\Services;
use App\Models\Order;
use Barryvdh\DomPDF\Facade\Pdf;
use chillerlan\QRCode\QRCode;
use chillerlan\QRCode\QROptions;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
class QrStickerService
{
/**
* Generate both SVG and A6 PDF sticker for an order
*/
public function generateSticker(Order $order): array
{
Log::info('Starting QR sticker generation', [
'order_uuid' => $order->uuid,
'order_number' => $order->order_number,
]);
try {
// Generate QR code URL
$qrUrl = route('ops.order.show', ['token' => $order->qr_token]);
Log::debug('Generating QR sticker', [
'order_uuid' => $order->uuid,
'order_number' => $order->order_number,
'qr_url' => $qrUrl,
]);
// Generate SVG (for web display)
$svgPath = $this->generateSvg($order, $qrUrl);
Log::info('SVG QR code generated', [
'order_uuid' => $order->uuid,
'svg_path' => $svgPath,
]);
// Generate A6 PDF (for printing)
$pdfPath = $this->generatePdf($order, $qrUrl);
Log::info('QR sticker generated successfully', [
'order_uuid' => $order->uuid,
'svg_path' => $svgPath,
'pdf_path' => $pdfPath,
]);
return [
'svg_path' => $svgPath,
'pdf_path' => $pdfPath,
];
} catch (\Exception $e) {
Log::error('Failed to generate QR sticker', [
'order_uuid' => $order->uuid,
'error' => $e->getMessage(),
]);
throw $e;
}
}
/**
* Generate SVG QR code for web display
*/
private function generateSvg(Order $order, string $qrUrl): string
{
$options = new QROptions([
'outputType' => QRCode::OUTPUT_MARKUP_SVG,
'eccLevel' => QRCode::ECC_H,
'scale' => 3,
'imageBase64' => false,
]);
$qrCode = new QRCode($options);
$qrSvg = $qrCode->render($qrUrl);
$path = "qr-codes/{$order->uuid}.svg";
Storage::disk('public')->put($path, $qrSvg);
return $path;
}
/**
* Generate A6 PDF sticker (105mm × 148mm) with QR code and readable info
*/
private function generatePdf(Order $order, string $qrUrl): string
{
// Generate inline QR code as SVG
$options = new QROptions([
'outputType' => QRCode::OUTPUT_MARKUP_SVG,
'eccLevel' => QRCode::ECC_H,
'scale' => 4,
'imageBase64' => false,
]);
$qrCode = new QRCode($options);
$qrSvg = $qrCode->render($qrUrl);
// Determine order type
$orderType = $order->type === 'standard' ? 'STOCK ORDER' : 'CUSTOM ORDER';
// Extract design name
$designName = '';
if ($order->items && $order->items->count() > 0) {
$firstItem = $order->items->first();
$designName = $firstItem->product?->name ?? $firstItem->name ?? 'Custom Design';
}
// Extract customer surname
$customerName = $order->user?->name ?? '';
$nameParts = explode(' ', trim($customerName));
$customerSurname = end($nameParts);
// Build HTML directly (no view file dependency)
$html = <<<HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: Arial, sans-serif; }
.sticker {
width: 148mm;
height: 105mm;
display: flex;
flex-direction: column;
padding: 5mm;
background: white;
}
.container {
display: flex;
gap: 3mm;
height: 100%;
}
.info-section {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
overflow: hidden;
}
.qr-section {
width: 55mm;
display: flex;
align-items: center;
justify-content: center;
}
.header {
border-bottom: 1pt solid #ddd;
padding-bottom: 2mm;
}
.order-type {
display: inline-block;
background: #333;
color: white;
padding: 1mm 2mm;
font-size: 7pt;
font-weight: bold;
letter-spacing: 0.5pt;
}
.order-number {
font-size: 9pt;
font-weight: bold;
margin-top: 1mm;
color: #222;
word-break: break-all;
}
.customer-info {
font-size: 8pt;
color: #555;
line-height: 1.3;
}
.customer-info .label {
font-size: 7pt;
color: #999;
text-transform: uppercase;
letter-spacing: 0.3pt;
}
.customer-info .name {
font-weight: 500;
color: #222;
}
.details {
font-size: 7.5pt;
color: #666;
line-height: 1.4;
}
.detail-row {
display: flex;
justify-content: space-between;
gap: 2mm;
margin-bottom: 1mm;
}
.detail-label {
font-weight: 500;
color: #999;
text-transform: uppercase;
font-size: 6.5pt;
flex: 1;
}
.detail-value {
text-align: right;
color: #222;
flex: 0 0 auto;
}
.qr-code {
width: 50mm;
height: 50mm;
display: flex;
align-items: center;
justify-content: center;
background: white;
}
.qr-code svg {
width: 100%;
height: 100%;
}
.footer {
font-size: 6pt;
color: #999;
text-align: center;
border-top: 0.5pt solid #eee;
padding-top: 1mm;
margin-top: auto;
}
</style>
</head>
<body>
<div class="sticker">
<div class="container">
<div class="info-section">
<div class="header">
<span class="order-type">{$orderType}</span>
<div class="order-number">{$order->order_number}</div>
</div>
<div class="customer-info">
<div class="label">Customer</div>
<div class="name">{$customerSurname}</div>
</div>
<div class="details">
<div class="detail-row">
<span class="detail-label">Design:</span>
<span class="detail-value" style="font-size: 6.5pt;">
{$this->truncate($designName, 12)}
</span>
</div>
<div class="detail-row">
<span class="detail-label">Date:</span>
<span class="detail-value">{$order->created_at->format('M d')}</span>
</div>
<div class="detail-row">
<span class="detail-label">Status:</span>
<span class="detail-value" style="text-transform: capitalize;">
{$this->formatStatus($order->status)}
</span>
</div>
</div>
<div class="footer">
Gen: {$order->created_at->format('Y-m-d H:i')}
</div>
</div>
<div class="qr-section">
<div class="qr-code">
{$qrSvg}
</div>
</div>
</div>
</div>
</body>
</html>
HTML;
try {
$pdf = Pdf::loadHTML($html);
$pdf->setPaper([0, 0, 420.94, 297.64], 'portrait');
$filename = "{$order->uuid}.pdf";
$path = "qr-stickers/{$filename}";
$pdfContent = $pdf->output();
Storage::disk('public')->put($path, $pdfContent);
return $path;
} catch (\Exception $e) {
Log::error('DomPDF generation failed', [
'order_uuid' => $order->uuid,
'error' => $e->getMessage(),
]);
throw $e;
}
}
/**
* Helper: Truncate string
*/
private function truncate($string, $length): string
{
return substr($string, 0, $length);
}
/**
* Helper: Format status text
*/
private function formatStatus($status): string
{
return str_replace('_', ' ', $status);
}
}