Files
Additional/resources/views/stickers/qr-sticker.blade.php
T
twotalesanimation 3040681842 feat: Add A6 PDF QR stickers with human-readable order info
Implement QrStickerService to generate both SVG (web) and PDF (print) stickers:

- Generate 32x32mm QR codes using SimpleSoftwareIO
- Create A6 (148mm  105mm) PDF stickers using DomPDF
- Include human-readable info: order number, customer, status, date, tracking
- Store order type (STOCK/CUSTOM) with visual badge
- Add generation timestamp to footer

Create stickers/qr-sticker.blade.php template:
- Two-column layout: info left, QR right
- Styled for A6 landscape printing
- Shows order number, customer, status, date, tracking info
- QR code positioned for easy scanning
- Print-optimized CSS

Update GenerateQrCodeOnOrderCreated listener:
- Now calls QrStickerService::generateSticker()
- Generates both SVG and PDF on OrderCreated event
- Logs paths to both formats

Add OpsController::downloadSticker() endpoint:
- GET /ops/orders/{id}/sticker/download
- Returns PDF with filename QR-{order_number}.pdf
- Requires ops access authorization
- Logs all downloads with user context

Update ops order-detail view:
- Add download button for A6 sticker PDF
- Position next to QR code display
- Link to new sticker download route
2026-01-02 19:51:23 +02:00

228 lines
6.0 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.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Arial, sans-serif;
}
/* A6 Landscape: 148mm × 105mm */
.sticker {
width: 148mm;
height: 105mm;
display: flex;
flex-direction: column;
padding: 5mm;
background: white;
position: relative;
}
/* Split layout: QR on right (50mm), info on left */
.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 with order type badge */
.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 */
.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;
}
/* Order details */
.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 */
.qr-code {
width: 50mm;
height: 50mm;
display: flex;
align-items: center;
justify-content: center;
background: white;
}
.qr-code svg {
width: 100%;
height: 100%;
}
/* Footer timestamp */
.footer {
font-size: 6pt;
color: #999;
text-align: center;
border-top: 0.5pt solid #eee;
padding-top: 1mm;
margin-top: auto;
}
/* Print settings */
@media print {
body {
margin: 0;
padding: 0;
}
.sticker {
page-break-after: avoid;
margin: 0;
box-shadow: none;
}
}
</style>
</head>
<body>
<div class="sticker">
<div class="container">
<div class="info-section">
<!-- Header -->
<div class="header">
<span class="order-type">{{ $orderType }}</span>
<div class="order-number">{{ $order->order_number }}</div>
</div>
<!-- Customer Info -->
<div class="customer-info">
<div class="label">Customer</div>
<div class="name">{{ $order->user->name ?? 'N/A' }}</div>
</div>
<!-- Order Details -->
<div class="details">
<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;">
{{ str_replace('_', ' ', $order->status) }}
</span>
</div>
@if($order->type === 'standard')
<div class="detail-row">
<span class="detail-label">Items:</span>
<span class="detail-value">{{ $order->items->count() }}</span>
</div>
@endif
@if($order->packing_completed_at)
<div class="detail-row">
<span class="detail-label">Packed:</span>
<span class="detail-value"></span>
</div>
@endif
@if($order->courier_tracking_number)
<div class="detail-row">
<span class="detail-label">Track:</span>
<span class="detail-value" style="font-size: 6.5pt;">{{ substr($order->courier_tracking_number, 0, 8) }}</span>
</div>
@endif
</div>
<!-- Footer -->
<div class="footer">
Gen: {{ $generatedAt }}
</div>
</div>
<!-- QR Code Section -->
<div class="qr-section">
<div class="qr-code">
{!! $qrSvg !!}
</div>
</div>
</div>
</div>
</body>
</html>