ifnex/04_Laravel/resources/views/pdfs/invoice.blade.php
Kazem Alghasi f12dff5338 - Add picqer/php-barcode-generator dependency
- Redesign AWB PDF with barcode, From/To, Value, Service, Type
- Redesign Invoice PDF with 9-row table, legal declaration, barcode
- Redesign Label PDF to A5 landscape with large barcode
- Add DOC/PARCEL condition for Invoice (only PARCEL has invoice)
- Fix ShipmentType enum names (DocNormal/DocEconomy)
- Add migration for shipment_items (row_number, unit_price)
- Add migration to make name nullable
- Update PdfService with base64 barcode embedding
- Update ShipmentPdfController with DOC type handling
- Add test_pdf_generation.php script

Phase 3.5 — PDF redesign complete"
2026-08-29 06:40:08 +03:30

364 lines
12 KiB
PHP

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: DejaVu Sans, Arial, sans-serif;
font-size: 10px;
color: #000;
background: #fff;
}
.page {
width: 210mm;
min-height: 297mm;
padding: 10mm;
margin: 0 auto;
}
/* ─── HEADER ─── */
.header {
text-align: center;
margin-bottom: 12px;
border-bottom: 2px solid #f37021;
padding-bottom: 8px;
}
.header h1 {
font-size: 22px;
color: #f37021;
margin-bottom: 4px;
letter-spacing: 2px;
}
.header-meta {
display: flex;
justify-content: space-between;
font-size: 10px;
color: #666;
}
.header-meta .awb-box {
background: #f9f9f9;
padding: 2px 8px;
border: 1px dashed #f37021;
font-weight: bold;
color: #000;
}
/* ─── SECTIONS ─── */
.two-col {
display: flex;
gap: 8px;
margin-bottom: 10px;
}
.section {
flex: 1;
border: 1px solid #000;
padding: 6px;
}
.section-title {
background: #f37021;
color: #fff;
padding: 3px 6px;
font-weight: bold;
font-size: 11px;
margin: -6px -6px 6px -6px;
}
.row {
display: flex;
margin-bottom: 3px;
font-size: 9px;
}
.label {
width: 110px;
font-weight: bold;
color: #444;
}
.value {
flex: 1;
color: #000;
}
/* ─── CONTENT ─── */
.content-section {
border: 1px solid #000;
padding: 6px;
margin-bottom: 10px;
}
.content-section .section-title {
background: #f37021;
color: #fff;
}
.content-desc {
font-size: 10px;
margin-bottom: 6px;
padding: 4px;
background: #f9f9f9;
}
/* ─── ITEMS TABLE ─── */
table {
width: 100%;
border-collapse: collapse;
font-size: 9px;
}
th, td {
border: 1px solid #ccc;
padding: 4px;
text-align: left;
}
th {
background: #f37021;
color: #fff;
font-weight: bold;
text-align: center;
}
td.text-center { text-align: center; }
td.text-right { text-align: right; }
.total-row {
background: #f9f9f9;
font-weight: bold;
}
/* ─── WEIGHT & DIMENSION ─── */
.weight-section {
border: 1px solid #000;
padding: 6px;
margin-bottom: 10px;
display: flex;
gap: 8px;
}
.weight-box {
flex: 1;
text-align: center;
padding: 6px;
border: 1px solid #ccc;
}
.weight-label {
font-size: 8px;
color: #666;
margin-bottom: 2px;
}
.weight-value {
font-size: 12px;
font-weight: bold;
color: #f37021;
}
/* ─── REASON FOR EXPORT ─── */
.reason-section {
border: 1px solid #000;
padding: 6px;
margin-bottom: 10px;
}
.reason-section .section-title {
background: #f37021;
color: #fff;
}
.reason-text {
font-size: 10px;
font-style: italic;
color: #333;
}
/* ─── DECLARATION ─── */
.declaration {
border: 1px solid #000;
padding: 8px;
margin-bottom: 10px;
font-size: 9px;
line-height: 1.6;
}
.declaration strong {
color: #f37021;
}
.signature-area {
margin-top: 8px;
display: flex;
justify-content: space-between;
}
.signature-box {
width: 45%;
text-align: center;
font-size: 9px;
}
.signature-line {
border-top: 1px solid #000;
margin-top: 25px;
padding-top: 3px;
}
/* ─── BOTTOM BARCODE ─── */
.bottom-barcode {
text-align: center;
margin-top: 10px;
padding: 6px;
border-top: 1px solid #ddd;
}
.bottom-barcode img {
height: 35px;
}
.bottom-info {
display: flex;
justify-content: space-between;
font-size: 9px;
color: #666;
margin-top: 6px;
padding: 0 6px;
}
/* ─── FOOTER ─── */
.footer {
margin-top: 10px;
border-top: 2px solid #f37021;
padding-top: 5px;
text-align: center;
font-size: 9px;
color: #999;
}
</style>
</head>
<body>
<div class="page">
<!-- HEADER -->
<div class="header">
<h1>INVOICE</h1>
<div class="header-meta">
<span>DATE: {{ $shipment->created_at?->format('m/d/Y') }}</span>
<span class="awb-box">INVOICE NO: {{ $shipment->awb_no }}</span>
</div>
</div>
<!-- SHIPPER & CONSIGNEE -->
<div class="two-col">
<div class="section">
<div class="section-title">SHIPPER</div>
<div class="row"><span class="label">SHIPPER:</span><span class="value">{{ $shipper['city'] }}, {{ $shipment->fromCountry?->name }} ({{ $shipment->fromCountry?->iso_code }})</span></div>
<div class="row"><span class="label">COMPANY NAME:</span><span class="value">{{ $shipper['company'] ?: '—' }}</span></div>
<div class="row"><span class="label">CONTACT PERSON:</span><span class="value">{{ $shipper['name'] }}</span></div>
<div class="row"><span class="label">ADDRESS:</span><span class="value">{{ $shipper['address'] }}</span></div>
<div class="row"><span class="label">PHONE:</span><span class="value">{{ $shipper['phone'] }}</span></div>
<div class="row"><span class="label">EMAIL:</span><span class="value">{{ $shipper['email'] ?: '—' }}</span></div>
</div>
<div class="section">
<div class="section-title">CONSIGNEE</div>
<div class="row"><span class="label">CONSIGNEE:</span><span class="value">{{ $receiver['city'] }}, {{ $shipment->toCountry?->name }} ({{ $shipment->toCountry?->iso_code }})</span></div>
<div class="row"><span class="label">COMPANY NAME:</span><span class="value">{{ $receiver['company'] ?: '—' }}</span></div>
<div class="row"><span class="label">CONTACT PERSON:</span><span class="value">{{ $receiver['name'] }}</span></div>
<div class="row"><span class="label">ADDRESS:</span><span class="value">{{ $receiver['address'] }}</span></div>
<div class="row"><span class="label">Zip Code &amp; City:</span><span class="value">{{ $receiver['zip'] }} {{ $receiver['city'] }}</span></div>
<div class="row"><span class="label">PHONE:</span><span class="value">{{ $receiver['phone'] }}</span></div>
<div class="row"><span class="label">EMAIL:</span><span class="value">{{ $receiver['email'] ?: '—' }}</span></div>
</div>
</div>
<!-- CONTENT -->
<div class="content-section">
<div class="section-title">Content</div>
<div class="content-desc">{{ $shipment->content_description ?: 'General Goods' }}</div>
<table>
<thead>
<tr>
<th style="width: 30px">NO</th>
<th>DESCRIPTION</th>
<th style="width: 90px">H.S. CODE</th>
<th style="width: 60px">QUANTITY</th>
<th style="width: 80px">UNIT PRICE</th>
<th style="width: 90px">TOTAL IN USD</th>
</tr>
</thead>
<tbody>
@forelse($items as $item)
<tr>
<td class="text-center">{{ $item->row_number }}</td>
<td>{{ $item->description }}</td>
<td class="text-center">{{ $item->hs_code }}</td>
<td class="text-center">{{ $item->quantity }}</td>
<td class="text-right">{{ number_format($item->unit_price, 2) }}</td>
<td class="text-right">{{ number_format($item->total_usd, 2) }}</td>
</tr>
@empty
<tr><td colspan="6" class="text-center">No items</td></tr>
@endforelse
{{-- Fill empty rows to match reference (9 rows) --}}
@for($i = $items->count() + 1; $i <= 9; $i++)
<tr>
<td class="text-center">{{ $i }}</td>
<td>&nbsp;</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
@endfor
<tr class="total-row">
<td colspan="5" class="text-right">TOTAL INVOICE AMOUNT IN USD</td>
<td class="text-right">{{ number_format($invoice_total_usd, 2) }}</td>
</tr>
</tbody>
</table>
</div>
<!-- WEIGHT & DIMENSION -->
<div class="weight-section">
<div class="weight-box">
<div class="weight-label">Gross Weight</div>
<div class="weight-value">{{ $shipment->weight }} KG</div>
</div>
<div class="weight-box">
<div class="weight-label">Volumetric Weight</div>
<div class="weight-value">{{ $shipment->volumetric_weight }} KG</div>
</div>
<div class="weight-box">
<div class="weight-label">W * L * H (cm)</div>
<div class="weight-value">{{ $shipment->dimensions ?: '—' }}</div>
</div>
</div>
<!-- REASON FOR EXPORT -->
<div class="reason-section">
<div class="section-title">REASON FOR EXPORT</div>
<div class="reason-text">
{{ strtoupper($shipment->reason_for_export ?: 'ITEM BEING SENT AS A SAMPLE, NOT FOR SALE') }}
</div>
</div>
<!-- DECLARATION -->
<div class="declaration">
<strong>I HEREBY STATE THAT THE ABOVE INFORMATION IS TRUE AND CORRECT TO THE BEST OF MY KNOWLEDGE AND I, THE UNDERSIGNED, AM LIABLE FOR ANY CONSEQUENCES THAT MAY ARISE DUE TO NON-DISCLOSURE OF FACTS.</strong>
<div class="signature-area">
<div class="signature-box">
NAME - SIGNATURE
<div class="signature-line"></div>
</div>
<div class="signature-box">
DATE
<div class="signature-line">...../...../..........</div>
</div>
</div>
</div>
<!-- BOTTOM BARCODE & INFO -->
<div class="bottom-barcode">
@if($barcode_base64)
<img src="{{ $barcode_base64 }}" alt="barcode">
@endif
<div class="bottom-info">
<span><strong>{{ $shipment->awb_no }}</strong></span>
<span>{{ $shipment->weight }} KG</span>
<span>{{ $shipment->created_at?->format('m/d/Y') }}</span>
<span>{{ $shipment->fromCountry?->name }} {{ $shipment->toCountry?->name }}</span>
</div>
</div>
<!-- FOOTER -->
<div class="footer">
IFNEX Logistics We Deliver Value | AWB: {{ $shipment->awb_no }} | Generated: {{ now()->format('Y-m-d H:i') }}
</div>
</div>
</body>
</html>