feat(shipment): expand schema and enhance PDF generation

Extend the shipment database schema to support additional tracking fields,
state information, and financial data. This update also transitions
shipment documentation from RTL/Persian to LTR/English layouts and
integrates barcode generation for AWB and labels.

- feat(db): add migration for missing shipment fields (state, cod_amount,
  declared_value, etc.)
- feat(shipment): update Shipment model with new casts and helper methods
- feat(ui): refactor Filament resource to use ShipmentStatus enum and
  add new input fields
- feat(pdf): implement barcode generation using picqer/php-barcode-generator
- feat(pdf): redesign AWB, Invoice, and Label templates for LTR/English
  support
- refactor(enum): simplify ShipmentStatus enum and update labels/colors
- chore(deps): add picqer/php-barcode-generator dependency
This commit is contained in:
Kazem Alghasi 2026-08-23 22:31:58 +03:30
parent 48c88e9bc7
commit b051ce4496
10 changed files with 559 additions and 407 deletions

View File

@ -4,78 +4,46 @@ namespace App\Enums;
enum ShipmentStatus: string
{
// ─── وضعیت‌های مشتری (قبل از پرداخت) ───
case PendingPayment = 'pending_payment'; // در انتظار پرداخت
case Cancelled = 'cancelled'; // لغو شده توسط مشتری
case PendingPayment = 'pending_payment';
case Cancelled = 'cancelled';
case Processed = 'processed';
case PickedUp = 'picked_up';
case InTransit = 'in_transit';
case OutForDelivery = 'out_for_delivery';
case Failed = 'failed';
case Delivered = 'delivered';
case Returned = 'returned';
case Archived = 'archived';
// ─── وضعیت‌های عملیاتی (بعد از پرداخت) ───
case Processed = 'processed'; // پردازش شده (ادمین تأیید کرده)
case PickedUp = 'picked_up'; // تحویل گرفته شده
case InTransit = 'in_transit'; // در حال حمل
case OutForDelivery = 'out_for_delivery'; // در مسیر تحویل
case Delivered = 'delivered'; // تحویل داده شده
case Failed = 'failed'; // ناموفق
case Returned = 'returned'; // برگشتی
/**
* لیبل فارسی
*/
public function label(): string
{
return match ($this) {
self::PendingPayment => 'در انتظار پرداخت',
self::Cancelled => 'لغو شده',
self::Processed => 'پردازش شده',
self::PickedUp => 'تحویل گرفته شده',
self::InTransit => 'در حال حمل',
self::OutForDelivery => 'در مسیر تحویل',
self::Delivered => 'تحویل داده شده',
self::Failed => 'ناموفق',
self::Returned => 'برگشتی',
self::Processed => 'Processed',
self::PickedUp => 'Picked Up',
self::InTransit => 'In Transit',
self::OutForDelivery => 'Out For Delivery',
self::Failed => 'Failed',
self::Delivered => 'Delivered',
self::Returned => 'Returned',
self::Archived => 'آرشیو',
};
}
/**
* رنگ badge در Filament
*/
public function color(): string
{
return match ($this) {
self::PendingPayment => 'warning',
self::Cancelled => 'gray',
self::Processed => 'info',
self::Cancelled => 'danger',
self::Processed => 'gray',
self::PickedUp => 'info',
self::InTransit => 'primary',
self::OutForDelivery => 'purple',
self::Delivered => 'success',
self::OutForDelivery => 'success',
self::Failed => 'danger',
self::Delivered => 'success',
self::Returned => 'warning',
self::Archived => 'gray',
};
}
/**
* آیا این وضعیت به معنای "پرداخت شده" است؟
*/
public function isPaid(): bool
{
return in_array($this, [
self::Processed,
self::PickedUp,
self::InTransit,
self::OutForDelivery,
self::Delivered,
self::Failed,
self::Returned,
]);
}
/**
* آیا این وضعیت قابل لغو توسط مشتری است؟
*/
public function canBeCancelledByCustomer(): bool
{
return in_array($this, [
self::PendingPayment,
]);
}
}

View File

@ -2,6 +2,7 @@
namespace App\Filament\Resources;
use App\Enums\ShipmentStatus;
use App\Filament\Resources\ShipmentResource\Pages;
use App\Filament\Resources\ShipmentResource\RelationManagers;
use App\Models\Shipment;
@ -44,40 +45,17 @@ class ShipmentResource extends Resource
'DOC_ECONOMY' => 'Doc Economy',
'PARCEL' => 'Parcel',
])
->required(),
->required()
->live(),
Forms\Components\Select::make('status')
->options([
'pending_payment' => 'در انتظار پرداخت',
'cancelled' => 'لغو شده',
'processed' => 'Processed',
'picked_up' => 'Picked Up',
'in_transit' => 'In Transit',
'out_for_delivery' => 'Out For Delivery',
'failed' => 'Failed',
'delivered' => 'Delivered',
'returned' => 'Returned',
])
->default('processed')
->required(),
Forms\Components\Select::make('status')
->options([
'processed' => 'Processed',
'picked_up' => 'Picked Up',
'in_transit' => 'In Transit',
'out_for_delivery' => 'Out For Delivery',
'failed' => 'Failed',
'delivered' => 'Delivered',
'returned' => 'Returned',
])
->options(collect(ShipmentStatus::cases())->mapWithKeys(fn ($case) => [
$case->value => $case->label(),
]))
->default('processed')
->required(),
Forms\Components\TextInput::make('reason_for_export')
->nullable()
->label('Reason for Export'),
Forms\Components\TextInput::make('forwarder')
->nullable()
->label('Forwarder'),
Forms\Components\Select::make('from_country_id')
->relationship('fromCountry', 'name')
->searchable()
@ -88,6 +66,9 @@ class ShipmentResource extends Resource
->searchable()
->preload()
->label('To Country'),
Forms\Components\TextInput::make('forwarder')
->nullable()
->label('Forwarder'),
])->columns(3),
Forms\Components\Section::make('Weight & Dimensions')
@ -110,6 +91,17 @@ class ShipmentResource extends Resource
Forms\Components\TextInput::make('dimensions')
->nullable()
->label('Dimensions (WxLxH)'),
Forms\Components\TextInput::make('declared_value')
->numeric()
->nullable()
->prefix('USD')
->label('Declared Value'),
Forms\Components\TextInput::make('content_description')
->nullable()
->label('Content Description'),
Forms\Components\TextInput::make('reason_for_export')
->nullable()
->label('Reason for Export'),
])->columns(4),
Forms\Components\Section::make('Financial Info')
@ -169,6 +161,11 @@ class ShipmentResource extends Resource
->nullable()
->prefix('ریال')
->label('Net Rial'),
Forms\Components\TextInput::make('cod_amount')
->numeric()
->default(0)
->prefix('AED')
->label('Cash on Delivery'),
])->columns(4),
Forms\Components\Section::make('Sender Info')
@ -193,6 +190,9 @@ class ShipmentResource extends Resource
Forms\Components\TextInput::make('sender_city')
->nullable()
->label('City'),
Forms\Components\TextInput::make('sender_state')
->nullable()
->label('State/Province'),
Forms\Components\TextInput::make('sender_zip')
->nullable()
->label('ZIP'),
@ -223,6 +223,9 @@ class ShipmentResource extends Resource
Forms\Components\TextInput::make('receiver_city')
->nullable()
->label('City'),
Forms\Components\TextInput::make('receiver_state')
->nullable()
->label('State/Province'),
Forms\Components\TextInput::make('receiver_zip')
->nullable()
->label('ZIP'),
@ -270,6 +273,10 @@ class ShipmentResource extends Resource
->reorderable()
->label('Items'),
]),
Forms\Components\Section::make('Customer Notes')->schema([
Forms\Components\Textarea::make('customer_notes')->nullable()->rows(3)->label('Customer Notes'),
]),
]);
}
@ -294,6 +301,8 @@ class ShipmentResource extends Resource
->label('Type'),
Tables\Columns\TextColumn::make('status')
->badge()
->color(fn (ShipmentStatus $state): string => $state->color())
->formatStateUsing(fn (ShipmentStatus $state): string => $state->label())
->searchable()
->label('Status'),
Tables\Columns\TextColumn::make('sender_name')
@ -315,17 +324,9 @@ class ShipmentResource extends Resource
])
->filters([
Tables\Filters\SelectFilter::make('status')
->options([
'pending_payment' => 'در انتظار پرداخت',
'cancelled' => 'لغو شده',
'processed' => 'Processed',
'picked_up' => 'Picked Up',
'in_transit' => 'In Transit',
'out_for_delivery' => 'Out For Delivery',
'failed' => 'Failed',
'delivered' => 'Delivered',
'returned' => 'Returned',
]),
->options(collect(ShipmentStatus::cases())->mapWithKeys(fn ($case) => [
$case->value => $case->label(),
])),
Tables\Filters\SelectFilter::make('type')
->options([
'DOC_NORMAL' => 'Doc Normal',

View File

@ -15,9 +15,10 @@ class Shipment extends Model
'warehousing_cost', 'vat_amount', 'discount', 'total_fee', 'net_dirham', 'net_rial',
'from_country_id', 'to_country_id',
'sender_name', 'sender_company', 'sender_phone', 'sender_email',
'sender_address', 'sender_city', 'sender_zip', 'sender_id_number',
'sender_address', 'sender_city', 'sender_state', 'sender_zip', 'sender_id_number',
'receiver_name', 'receiver_company', 'receiver_phone', 'receiver_email',
'receiver_address', 'receiver_city', 'receiver_zip', 'receiver_id_number',
'receiver_address', 'receiver_city', 'receiver_state', 'receiver_zip', 'receiver_id_number',
'customer_notes', 'cod_amount', 'declared_value', 'content_description',
];
protected function casts(): array
@ -26,6 +27,22 @@ class Shipment extends Model
'direction' => \App\Enums\ShipmentDirection::class,
'type' => \App\Enums\ShipmentType::class,
'status' => \App\Enums\ShipmentStatus::class,
'weight' => 'decimal:2',
'volumetric_weight' => 'decimal:2',
'chargeable_weight' => 'decimal:2',
'shipping_price' => 'decimal:2',
'extra_service' => 'decimal:2',
'packing_cost' => 'decimal:2',
'domestic_pickup' => 'decimal:2',
'domestic_delivery' => 'decimal:2',
'warehousing_cost' => 'decimal:2',
'vat_amount' => 'decimal:2',
'discount' => 'decimal:2',
'total_fee' => 'decimal:2',
'net_dirham' => 'decimal:2',
'net_rial' => 'decimal:2',
'cod_amount' => 'decimal:2',
'declared_value' => 'decimal:2',
];
}
@ -58,4 +75,19 @@ class Shipment extends Model
{
return $this->hasMany(ShipmentTrackingEvent::class);
}
public function isParcel(): bool
{
return $this->type === \App\Enums\ShipmentType::Parcel;
}
public function isDocument(): bool
{
return $this->type !== \App\Enums\ShipmentType::Parcel;
}
public function getInvoiceTotalUsdAttribute(): float
{
return $this->items->sum('total_usd');
}
}

View File

@ -3,10 +3,10 @@
namespace App\Services;
use App\Models\Shipment;
use App\Models\ShipmentItem;
use Dompdf\Dompdf;
use Dompdf\Options;
use Illuminate\Support\Collection;
use Picqer\Barcode\BarcodeGeneratorHTML;
class PdfService
{
@ -19,12 +19,10 @@ class PdfService
'shipper' => $this->formatAddress($shipment, 'sender'),
'receiver' => $this->formatAddress($shipment, 'receiver'),
'items' => $shipment->items ?? collect(),
'invoice_total_usd' => optional($shipment->items)->sum('total_usd') ?? 0,
'barcode' => $this->generateBarcode($shipment->awb_no),
];
$html = view('pdfs.awb', $data)->render();
return $this->generatePdf($html, 'A4', 'portrait');
return $this->generatePdf(view('pdfs.awb', $data)->render(), 'A4', 'portrait');
}
public function invoice(Shipment $shipment): string
@ -35,13 +33,12 @@ class PdfService
'shipment' => $shipment,
'shipper' => $this->formatAddress($shipment, 'sender'),
'receiver' => $this->formatAddress($shipment, 'receiver'),
'items' => $shipment->items ?? collect(),
'invoice_total_usd' => optional($shipment->items)->sum('total_usd') ?? 0,
'items' => $shipment->isParcel() ? ($shipment->items ?? collect()) : collect(),
'invoice_total_usd' => $shipment->invoice_total_usd,
'barcode' => $this->generateBarcode($shipment->awb_no),
];
$html = view('pdfs.invoice', $data)->render();
return $this->generatePdf($html, 'A4', 'portrait');
return $this->generatePdf(view('pdfs.invoice', $data)->render(), 'A4', 'portrait');
}
public function label(Shipment $shipment): string
@ -52,14 +49,13 @@ class PdfService
'shipment' => $shipment,
'shipper' => $this->formatAddress($shipment, 'sender'),
'receiver' => $this->formatAddress($shipment, 'receiver'),
'barcode' => $this->generateBarcode($shipment->awb_no),
];
$html = view('pdfs.label', $data)->render();
return $this->generatePdf($html, 'A4', 'portrait');
return $this->generatePdf(view('pdfs.label', $data)->render(), 'A4', 'portrait');
}
private function generatePdf(string $html, array|string $paper, string $orientation): string
private function generatePdf(string $html, string $paper, string $orientation): string
{
$options = new Options();
$options->set('isHtml5ParserEnabled', true);
@ -68,13 +64,7 @@ class PdfService
$dompdf = new Dompdf($options);
$dompdf->loadHtml($html);
if (is_array($paper)) {
$dompdf->setPaper($paper, $orientation);
} else {
$dompdf->setPaper($paper, $orientation);
}
$dompdf->render();
return $dompdf->output();
@ -83,14 +73,25 @@ class PdfService
private function formatAddress(Shipment $shipment, string $prefix): Collection
{
return collect([
'name' => $shipment->{$prefix . '_name'},
'company' => $shipment->{$prefix . '_company'},
'phone' => $shipment->{$prefix . '_phone'},
'email' => $shipment->{$prefix . '_email'},
'address' => $shipment->{$prefix . '_address'},
'city' => $shipment->{$prefix . '_city'},
'zip' => $shipment->{$prefix . '_zip'},
'id_number' => $shipment->{$prefix . '_id_number'},
'name' => $shipment->{"{$prefix}_name"},
'company' => $shipment->{"{$prefix}_company"},
'phone' => $shipment->{"{$prefix}_phone"},
'email' => $shipment->{"{$prefix}_email"},
'address' => $shipment->{"{$prefix}_address"},
'city' => $shipment->{"{$prefix}_city"},
'state' => $shipment->{"{$prefix}_state"},
'zip' => $shipment->{"{$prefix}_zip"},
'id_number' => $shipment->{"{$prefix}_id_number"},
]);
}
private function generateBarcode(string $code): string
{
try {
$generator = new BarcodeGeneratorHTML();
return $generator->getBarcode($code, $generator::TYPE_CODE_128, 2, 60);
} catch (\Throwable $e) {
return '<div style="color:#999;font-size:10px;">Barcode unavailable</div>';
}
}
}

View File

@ -14,6 +14,7 @@
"laravel/tinker": "^2.10.1",
"maatwebsite/excel": "^3.1",
"morilog/jalali": "^3.0",
"picqer/php-barcode-generator": "^3.3",
"spatie/laravel-permission": "^6.25"
},
"require-dev": {

View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "b8dff8147ac7f29e9282deaf74979683",
"content-hash": "a22b2cc91f8cab3cca421c6ab381c544",
"packages": [
{
"name": "anourvalar/eloquent-serialize",
@ -4914,6 +4914,86 @@
],
"time": "2025-12-27T19:41:33+00:00"
},
{
"name": "picqer/php-barcode-generator",
"version": "v3.3.0",
"source": {
"type": "git",
"url": "https://github.com/picqer/php-barcode-generator.git",
"reference": "6beada2a30623832ff41b8c7d307c169736b8552"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/picqer/php-barcode-generator/zipball/6beada2a30623832ff41b8c7d307c169736b8552",
"reference": "6beada2a30623832ff41b8c7d307c169736b8552",
"shasum": ""
},
"require": {
"php": "^8.2"
},
"require-dev": {
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.5"
},
"suggest": {
"ext-bcmath": "Barcode IMB (Intelligent Mail Barcode) needs bcmath extension",
"ext-gd": "For JPG and PNG generators, GD or Imagick is required",
"ext-imagick": "For JPG and PNG generators, GD or Imagick is required"
},
"type": "library",
"autoload": {
"psr-4": {
"Picqer\\Barcode\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0-or-later"
],
"authors": [
{
"name": "Casper Bakker",
"email": "info@picqer.com",
"homepage": "https://picqer.com"
},
{
"name": "Nicola Asuni",
"email": "info@tecnick.com",
"homepage": "http://nicolaasuni.tecnick.com"
}
],
"description": "An easy to use, non-bloated, barcode generator in PHP. Creates SVG, PNG, JPG and HTML images from the most used 1D barcode standards.",
"homepage": "https://github.com/picqer/php-barcode-generator",
"keywords": [
"CODABAR",
"Code11",
"Code93",
"EAN13",
"KIX",
"KIXCODE",
"MSI",
"POSTNET",
"Pharma",
"Standard 2 of 5",
"barcode",
"barcode generator",
"code128",
"code39",
"ean",
"html",
"jpeg",
"jpg",
"php",
"png",
"svg",
"upc"
],
"support": {
"issues": "https://github.com/picqer/php-barcode-generator/issues",
"source": "https://github.com/picqer/php-barcode-generator/tree/v3.3.0"
},
"time": "2026-08-22T09:50:32+00:00"
},
{
"name": "psr/cache",
"version": "3.0.0",

View File

@ -0,0 +1,70 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('shipments', function (Blueprint $table) {
// فیلدهای آدرس — State/Province
$table->string('sender_state')->nullable()->after('sender_city');
$table->string('receiver_state')->nullable()->after('receiver_city');
// یادداشت مشتری
$table->text('customer_notes')->nullable()->after('receiver_id_number');
// Cash on Delivery (AED) — از اکسل مرجع: ستون AP = COD
$table->decimal('cod_amount', 12, 2)->default(0)->unsigned()->after('net_rial');
// ارزش اظهار شده (USD) — از اکسل مرجع: ستون K = Value
$table->decimal('declared_value', 12, 2)->nullable()->unsigned()->after('dimensions');
// توضیحات محموله — از اکسل مرجع: ستون L = Content
$table->string('content_description')->nullable()->after('declared_value');
});
// به‌روزرسانی enum status برای افزودن مقادیر جدید
DB::statement("ALTER TABLE shipments MODIFY COLUMN status ENUM(
'pending_payment',
'cancelled',
'processed',
'picked_up',
'in_transit',
'out_for_delivery',
'failed',
'delivered',
'returned',
'archived'
) DEFAULT 'processed'");
}
public function down(): void
{
Schema::table('shipments', function (Blueprint $table) {
$table->dropColumn([
'sender_state',
'receiver_state',
'customer_notes',
'cod_amount',
'declared_value',
'content_description',
]);
});
DB::statement("ALTER TABLE shipments MODIFY COLUMN status ENUM(
'pending_payment',
'cancelled',
'processed',
'picked_up',
'in_transit',
'out_for_delivery',
'failed',
'delivered',
'returned'
) DEFAULT 'pending_payment'");
}
};

View File

@ -1,69 +1,43 @@
<!DOCTYPE html>
<!--
AWB PDF TEMPLATE TODO: تطبیق نهایی با قالب اکسل
Excel Reference: شیت «AWB» در فایل 01KYWGVNKS5TNMN37RV77PCYNZ.xlsx
Cell Mapping:
- A1: لوگوی IFNEX (image2.png, 169x159px)
- C1:D1: «Shipment Waybill»
- K11:K12: AWB Number (lookup key)
- A6:A13: SHIPPER section
- D6:D13: RECEIVER section
- A14:A22: SHIPMENT section
- D14:D22: PAYMENT section
- D24:E24: Shipper Signature line
- D28:E28: Track URL
VLOOKUP Source: شیت «List» (A3:AD3938)
Required Fields:
- Shipper: Name, Company, Phone, Email, Address, City, Zip, ID Number
- Receiver: Name, Company, Phone, Email, Address, City, Zip
- Shipment: Gross Weight, Volumetric Weight, Chargeable Weight, Dimensions, Service Type, Direction, From/To Country, Forwarder, Content
- Payment: Shipping Price (AED), Extra Service (IRR), Packing Cost (IRR), Domestic Pickup (IRR), Domestic Delivery (IRR), Warehousing Cost (IRR), Discount (IRR), Total Fee (IRR), Net Dirham (AED)
Font: باید فونت رسمی IFNEX استفاده شود (در حال حاضر DejaVu Sans جایگزین شده)
Colors: عنوان‌ها نارنجی #f37021، حاشیه‌ها #ddd، پس‌زمینه مالی #f9f9f9
Page: A4 Portrait
-->
<html lang="fa" dir="rtl">
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: DejaVu Sans, sans-serif; font-size: 11px; direction: rtl; text-align: right; }
.container { width: 210mm; min-height: 297mm; padding: 15mm; margin: 0 auto; }
body { font-family: DejaVu Sans, sans-serif; font-size: 11px; direction: ltr; text-align: left; color: #333; }
.container { width: 190mm; min-height: 277mm; padding: 10mm; margin: 0 auto; }
.header { display: flex; align-items: center; margin-bottom: 15px; border-bottom: 2px solid #f37021; padding-bottom: 10px; }
.logo { width: 60px; height: auto; margin-left: 10px; }
.title-area { flex: 1; }
.title { font-size: 18px; font-weight: bold; color: #f37021; }
.header { display: flex; align-items: center; margin-bottom: 12px; border-bottom: 2px solid #f37021; padding-bottom: 8px; }
.logo { width: 70px; height: auto; }
.title-area { flex: 1; text-align: center; }
.title { font-size: 20px; font-weight: bold; color: #f37021; }
.subtitle { font-size: 12px; color: #666; }
.awb-number { font-size: 14px; font-weight: bold; color: #f37021; text-align: right; }
.awb-number { font-size: 14px; font-weight: bold; color: #f37021; margin-top: 5px; }
.section { border: 1px solid #ccc; padding: 8px; margin-bottom: 10px; }
.section-title { font-weight: bold; color: #f37021; margin-bottom: 6px; font-size: 12px; border-bottom: 1px solid #eee; padding-bottom: 3px; }
.main-content { display: flex; gap: 15px; margin-bottom: 15px; }
.section { flex: 1; border: 1px solid #ddd; padding: 10px; }
.section-title { font-weight: bold; color: #f37021; margin-bottom: 8px; font-size: 12px; border-bottom: 1px solid #eee; padding-bottom: 3px; }
.row { margin-bottom: 4px; }
.label { font-weight: bold; color: #555; display: inline-block; width: 100px; }
.value { display: inline-block; }
.shipment-details { border: 1px solid #ddd; padding: 10px; margin-bottom: 15px; }
.shipment-title { font-weight: bold; color: #f37021; margin-bottom: 8px; font-size: 12px; }
.payment-section { border: 1px solid #ddd; padding: 10px; margin-bottom: 15px; background-color: #f9f9f9; }
.payment-title { font-weight: bold; color: #f37021; margin-bottom: 8px; font-size: 12px; }
.two-col { display: flex; gap: 20px; }
.two-col { display: flex; gap: 10px; margin-bottom: 10px; }
.col { flex: 1; }
.signature { margin-top: 20px; display: flex; justify-content: space-between; }
.row { margin-bottom: 3px; }
.label { font-weight: bold; color: #555; display: inline-block; width: 110px; }
.value { display: inline-block; }
.payment-section { border: 1px solid #ccc; padding: 8px; margin-bottom: 10px; background-color: #f9f9f9; }
.payment-title { font-weight: bold; color: #f37021; margin-bottom: 6px; font-size: 12px; }
.total-row { font-weight: bold; color: #f37021; font-size: 12px; border-top: 1px solid #ddd; padding-top: 4px; margin-top: 4px; }
.disclaimer { background-color: #fafafa; border: 1px solid #eee; padding: 8px; margin-bottom: 10px; font-size: 8px; color: #666; line-height: 1.4; }
.signature { margin-top: 15px; display: flex; justify-content: space-between; }
.signature-box { width: 45%; border-top: 1px solid #333; padding-top: 5px; text-align: center; font-size: 10px; }
.footer { margin-top: 15px; text-align: center; font-size: 9px; color: #999; border-top: 1px solid #ddd; padding-top: 10px; }
.footer { margin-top: 10px; text-align: center; font-size: 10px; color: #999; border-top: 1px solid #ddd; padding-top: 8px; }
table { width: 100%; border-collapse: collapse; }
td { vertical-align: top; padding: 0; }
</style>
</head>
<body>
@ -72,73 +46,112 @@
<img src="{{ public_path('logo.png') }}" class="logo" alt="IFNEX Logo">
<div class="title-area">
<div class="title">Shipment Waybill</div>
<div class="subtitle">We Deliver Value</div>
</div>
<div class="awb-number">AWB No: {{ $shipment->awb_no }}</div>
</div>
</div>
<div class="main-content">
<table>
<tr>
<td width="50%" style="padding-right: 5px;">
<div class="section">
<div class="section-title">SHIPPER</div>
<div class="row"><span class="label">Name:</span><span class="value">{{ $shipper['name'] }}</span></div>
<div class="row"><span class="label">Company:</span><span class="value">{{ $shipper['company'] }}</span></div>
<div class="row"><span class="label">Phone:</span><span class="value">{{ $shipper['phone'] }}</span></div>
<div class="row"><span class="label">From:</span><span class="value">{{ $shipment->fromCountry?->name }}</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 Name:</span><span class="value">{{ $shipper['name'] }}</span></div>
<div class="row"><span class="label">Tel/Mob:</span><span class="value">{{ $shipper['phone'] }}</span></div>
<div class="row"><span class="label">Email:</span><span class="value">{{ $shipper['email'] }}</span></div>
<div class="row"><span class="label">Address:</span><span class="value">{{ $shipper['address'] }}</span></div>
<div class="row"><span class="label">City:</span><span class="value">{{ $shipper['city'] }}</span></div>
<div class="row"><span class="label">Zip:</span><span class="value">{{ $shipper['zip'] }}</span></div>
<div class="row"><span class="label">ID:</span><span class="value">{{ $shipper['id_number'] }}</span></div>
<div class="row"><span class="label">ID Number:</span><span class="value">{{ $shipper['id_number'] }}</span></div>
<div class="row"><span class="label">Zip Code:</span><span class="value">{{ $shipper['zip'] }}</span></div>
</div>
</td>
<td width="50%" style="padding-left: 5px;">
<div class="section">
<div class="section-title">RECEIVER</div>
<div class="row"><span class="label">Name:</span><span class="value">{{ $receiver['name'] }}</span></div>
<div class="row"><span class="label">Company:</span><span class="value">{{ $receiver['company'] }}</span></div>
<div class="row"><span class="label">Phone:</span><span class="value">{{ $receiver['phone'] }}</span></div>
<div class="row"><span class="label">To:</span><span class="value">{{ $shipment->toCountry?->name }}</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 Name:</span><span class="value">{{ $receiver['name'] }}</span></div>
<div class="row"><span class="label">Tel/Mobile:</span><span class="value">{{ $receiver['phone'] }}</span></div>
<div class="row"><span class="label">Email:</span><span class="value">{{ $receiver['email'] }}</span></div>
<div class="row"><span class="label">Address:</span><span class="value">{{ $receiver['address'] }}</span></div>
<div class="row"><span class="label">City:</span><span class="value">{{ $receiver['city'] }}</span></div>
<div class="row"><span class="label">Zip:</span><span class="value">{{ $receiver['zip'] }}</span></div>
<div class="row"><span class="label">ID:</span><span class="value">{{ $receiver['id_number'] }}</span></div>
</div>
<div class="row"><span class="label">Zip Code:</span><span class="value">{{ $receiver['zip'] }}</span></div>
</div>
</td>
</tr>
</table>
<div class="shipment-details">
<div class="shipment-title">SHIPMENT</div>
<div class="two-col">
<div class="col">
<div class="row"><span class="label">Gross Weight:</span><span class="value">{{ $shipment->weight }} kg</span></div>
<div class="row"><span class="label">Volumetric Weight:</span><span class="value">{{ $shipment->volumetric_weight }} kg</span></div>
<div class="row"><span class="label">Chargeable Weight:</span><span class="value">{{ $shipment->chargeable_weight }} kg</span></div>
<div class="row"><span class="label">Dimensions:</span><span class="value">{{ $shipment->dimensions }}</span></div>
<table>
<tr>
<td width="50%" style="padding-right: 5px;">
<div class="section">
<div class="section-title">SHIPMENT</div>
<div class="row"><span class="label">Gross Weight:</span><span class="value">{{ $shipment->weight }} Kg</span></div>
<div class="row"><span class="label">Dimensions:</span><span class="value">{{ $shipment->dimensions }} cm</span></div>
<div class="row"><span class="label">Volumetric Weight:</span><span class="value">{{ $shipment->volumetric_weight }} Kg</span></div>
<div class="row"><span class="label">Chargeable Weight:</span><span class="value">{{ $shipment->chargeable_weight }} Kg</span></div>
<div class="row"><span class="label">Value:</span><span class="value">${{ number_format($shipment->declared_value ?? 0, 2) }} USD</span></div>
<div class="row"><span class="label">Service:</span><span class="value">{{ $shipment->type?->label() }}</span></div>
<div class="row"><span class="label">Direction:</span><span class="value">{{ $shipment->direction?->label() }}</span></div>
</div>
<div class="col">
<div class="row"><span class="label">From:</span><span class="value">{{ $shipment->fromCountry?->name }}</span></div>
<div class="row"><span class="label">To:</span><span class="value">{{ $shipment->toCountry?->name }}</span></div>
<div class="row"><span class="label">Forwarder:</span><span class="value">{{ $shipment->forwarder }}</span></div>
<div class="row"><span class="label">Content:</span><span class="value">{{ $shipment->reason_for_export ?: 'General Goods' }}</span></div>
</div>
</div>
</div>
<div class="row"><span class="label">Type:</span><span class="value">{{ $shipment->direction?->value === 'export' ? 'Export' : 'Import' }}</span></div>
<div class="row"><span class="label">Content:</span><span class="value">{{ $shipment->content_description ?: 'General Goods' }}</span></div>
@if($shipment->isParcel() && $items->count() > 0)
<div style="margin-top: 8px; border-top: 1px solid #eee; padding-top: 6px;">
<div class="section-title" style="margin-bottom: 4px;">Shipment Details</div>
<table style="width: 100%; font-size: 10px; border-collapse: collapse;">
<thead>
<tr style="background-color: #f37021; color: #fff;">
<th style="border: 1px solid #ddd; padding: 3px; text-align: left;">Row</th>
<th style="border: 1px solid #ddd; padding: 3px; text-align: left;">Description</th>
<th style="border: 1px solid #ddd; padding: 3px; text-align: left;">HS Code</th>
<th style="border: 1px solid #ddd; padding: 3px; text-align: left;">Qty</th>
<th style="border: 1px solid #ddd; padding: 3px; text-align: left;">Unit Price (USD)</th>
<th style="border: 1px solid #ddd; padding: 3px; text-align: left;">Total (USD)</th>
</tr>
</thead>
<tbody>
@foreach($items as $item)
<tr>
<td style="border: 1px solid #ddd; padding: 3px;">{{ $item->row_number }}</td>
<td style="border: 1px solid #ddd; padding: 3px;">{{ $item->description }}</td>
<td style="border: 1px solid #ddd; padding: 3px;">{{ $item->hs_code }}</td>
<td style="border: 1px solid #ddd; padding: 3px;">{{ $item->quantity }}</td>
<td style="border: 1px solid #ddd; padding: 3px;">{{ number_format($item->unit_price, 2) }}</td>
<td style="border: 1px solid #ddd; padding: 3px;">{{ number_format($item->total_usd, 2) }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
</div>
</td>
<td width="50%" style="padding-left: 5px;">
<div class="payment-section">
<div class="payment-title">PAYMENT</div>
<div class="row"><span class="label">Shipping Price (AED):</span><span class="value">{{ number_format($shipment->shipping_price ?? 0, 2) }}</span></div>
<div class="row"><span class="label">Extra Service (IRR):</span><span class="value">{{ number_format($shipment->extra_service ?? 0) }}</span></div>
<div class="row"><span class="label">Packing Cost (IRR):</span><span class="value">{{ number_format($shipment->packing_cost ?? 0) }}</span></div>
<div class="row"><span class="label">Domestic Pickup (IRR):</span><span class="value">{{ number_format($shipment->domestic_pickup ?? 0) }}</span></div>
<div class="row"><span class="label">Domestic Delivery (IRR):</span><span class="value">{{ number_format($shipment->domestic_delivery ?? 0) }}</span></div>
<div class="row"><span class="label">Warehousing (IRR):</span><span class="value">{{ number_format($shipment->warehousing_cost ?? 0) }}</span></div>
<div class="row"><span class="label">Discount (IRR):</span><span class="value">{{ number_format($shipment->discount ?? 0) }}</span></div>
<div class="row" style="font-weight: bold; color: #f37021; font-size: 12px;"><span class="label">Total Fee (IRR):</span><span class="value">{{ number_format($shipment->total_fee ?? 0) }}</span></div>
<div class="row" style="font-weight: bold; color: #f37021; font-size: 12px;"><span class="label">Net Dirham (AED):</span><span class="value">{{ number_format($shipment->net_dirham ?? 0, 2) }}</span></div>
<div class="row"><span class="label">Shipping Price:</span><span class="value">{{ number_format($shipment->shipping_price ?? 0, 2) }} IRR</span></div>
<div class="row"><span class="label">Extra Service:</span><span class="value">{{ number_format($shipment->extra_service ?? 0) }} IRR</span></div>
<div class="row"><span class="label">Domestic Pickup:</span><span class="value">{{ number_format($shipment->domestic_pickup ?? 0) }} IRR</span></div>
<div class="row"><span class="label">Packing Cost:</span><span class="value">{{ number_format($shipment->packing_cost ?? 0) }} IRR</span></div>
<div class="row"><span class="label">Domestic Delivery:</span><span class="value">{{ number_format($shipment->domestic_delivery ?? 0) }} IRR</span></div>
<div class="row"><span class="label">Warehousing Cost:</span><span class="value">{{ number_format($shipment->warehousing_cost ?? 0) }} IRR</span></div>
<div class="row"><span class="label">Discount:</span><span class="value">{{ number_format($shipment->discount ?? 0) }} IRR</span></div>
<div class="row total-row"><span class="label">Total Fee:</span><span class="value">{{ number_format($shipment->total_fee ?? 0) }} IRR</span></div>
<div class="row total-row"><span class="label">Cash on Delivery:</span><span class="value">{{ number_format($shipment->cod_amount ?? 0, 2) }} AED</span></div>
</div>
</td>
</tr>
</table>
<div class="disclaimer">
Please note that: Door-to-door Services is only applicable when the shipment is general cargo. For special cargo, dangerous goods, or items requiring special handling, additional charges may apply. The receiver is responsible for customs clearance and any associated fees. Claims must be filed within 30 days of delivery. Shipper declares that the contents are accurately described and properly packed for transport.
</div>
<div class="signature">
<div class="signature-box">
Shipper Name & Signature<br>
Date: {{ now()->format('Y-m-d') }}
Date: ....../....../........
</div>
<div class="signature-box">
Track Your Shipment at:<br>

View File

@ -1,157 +1,164 @@
<!DOCTYPE html>
<!--
INVOICE PDF TEMPLATE TODO: تطبیق نهایی با قالب اکسل
Excel Reference: شیت «INVOICE + label» در فایل 01KYWGVNKS5TNMN37RV77PCYNZ.xlsx
Cell Mapping:
- B1: «INVOICE»
- E2:F2: DATE
- F3: INVOICE NO (AWB number, lookup key)
- A4:B9: SHIPPER info (Name, Company, Contact, Address, City/Zip, Email)
- A10:B16: CONSIGNEE info
- A18:F18: Content description
- A20:F20: Items table headers (NO, DESCRIPTION, H.S. Code, QUANTITY, UNIT PRICE, TOTAL IN USD)
- A21:F29: 9 item rows (VLOOKUP from List sheet)
- C30:F30: TOTAL INVOICE AMOUNT IN USD
- A31:B32: Weight & Dimension
- A34:B34: REASON FOR EXPORT
- A36:F36: Legal declaration
- F40:F45: AWB echo, Weight, Origin/Destination country
VLOOKUP Source: شیت «List» (A3:CH3938)
Required Fields:
- Shipper: Name, Company, Contact, Address, City/Zip, Email
- Consignee: Name, Company, Contact, Address, City/Zip, Email
- Content: Description, Reason for Export
- Items: up to 9 rows (Description, HS Code, Quantity, Unit Price, Total USD)
- Shipment: Weight, Volumetric Weight, Dimensions, Service, Direction
- Total Invoice Amount in USD
Font: باید فونت رسمی IFNEX استفاده شود
Colors: عنوان‌ها نارنجی #f37021، جدول‌ها با حاشیه #ddd
Page: A4 Portrait
-->
<html lang="fa" dir="rtl">
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: DejaVu Sans, sans-serif; font-size: 11px; direction: rtl; text-align: right; }
.container { padding: 20px; }
.header { text-align: center; margin-bottom: 20px; border-bottom: 2px solid #f37021; padding-bottom: 10px; }
.header h1 { color: #f37021; font-size: 20px; margin-bottom: 5px; }
.header .meta { display: flex; justify-content: space-between; font-size: 10px; color: #666; }
.section { margin-bottom: 15px; border: 1px solid #ddd; padding: 10px; }
.section-title { font-weight: bold; color: #f37021; margin-bottom: 8px; font-size: 12px; border-bottom: 1px solid #eee; padding-bottom: 3px; }
.row { display: flex; margin-bottom: 4px; }
.label { width: 130px; font-weight: bold; color: #555; }
.value { flex: 1; }
.two-col { display: flex; gap: 20px; }
body { font-family: DejaVu Sans, sans-serif; font-size: 11px; direction: ltr; text-align: left; color: #333; }
.container { width: 190mm; min-height: 277mm; padding: 10mm; margin: 0 auto; }
.header { text-align: center; margin-bottom: 12px; border-bottom: 2px solid #f37021; padding-bottom: 8px; position: relative; }
.header h1 { color: #f37021; font-size: 24px; font-weight: bold; }
.header .meta { display: flex; justify-content: space-between; font-size: 10px; color: #666; margin-top: 5px; }
.section { border: 1px solid #ccc; padding: 8px; margin-bottom: 10px; }
.section-title { font-weight: bold; color: #f37021; margin-bottom: 6px; font-size: 12px; border-bottom: 1px solid #eee; padding-bottom: 3px; }
.two-col { display: flex; gap: 10px; margin-bottom: 10px; }
.col { flex: 1; }
.row { margin-bottom: 3px; }
.label { font-weight: bold; color: #555; display: inline-block; width: 110px; }
.value { display: inline-block; }
table { width: 100%; border-collapse: collapse; margin-top: 10px; }
th, td { border: 1px solid #ddd; padding: 6px; text-align: right; font-size: 10px; }
th { background-color: #f5f5f5; font-weight: bold; }
.text-right { text-align: right; }
.text-center { text-align: center; }
.total { font-weight: bold; color: #f37021; }
.footer { margin-top: 20px; border-top: 1px solid #ddd; padding-top: 10px; font-size: 9px; color: #666; }
.declaration { font-size: 9px; color: #666; margin-top: 15px; line-height: 1.5; }
.awb-box { background-color: #f9f9f9; padding: 5px 10px; display: inline-block; border: 1px dashed #f37021; }
th, td { border: 1px solid #ddd; padding: 5px; text-align: left; font-size: 10px; }
th { background-color: #f5f5f5; font-weight: bold; color: #333; }
.content-box { border: 1px solid #ccc; padding: 8px; margin-bottom: 10px; }
.content-title { font-weight: bold; color: #f37021; margin-bottom: 6px; font-size: 12px; }
.info-row { display: flex; justify-content: space-between; margin-bottom: 10px; }
.info-box { flex: 1; border: 1px solid #ccc; padding: 8px; }
.declaration { border: 1px solid #ddd; padding: 10px; margin-bottom: 10px; font-size: 10px; color: #555; text-align: center; line-height: 1.5; }
.signature-line { margin-top: 15px; display: flex; justify-content: space-between; }
.signature-box { width: 45%; border-top: 1px solid #333; padding-top: 5px; text-align: center; font-size: 10px; }
.label-section { border: 2px solid #000; padding: 10px; margin-bottom: 10px; }
.label-title { font-weight: bold; font-size: 12px; margin-bottom: 6px; }
.label-row { display: flex; justify-content: space-between; margin-bottom: 5px; }
.barcode { text-align: center; margin-top: 8px; }
.footer { margin-top: 10px; text-align: center; font-size: 10px; color: #999; border-top: 1px solid #ddd; padding-top: 8px; }
.total-row { font-weight: bold; color: #f37021; font-size: 12px; border-top: 1px solid #ddd; padding-top: 4px; margin-top: 4px; text-align: right; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>INVOICE / فاکتور</h1>
<h1>INVOICE</h1>
<div class="meta">
<span>DATE: {{ $shipment->created_at?->format('Y-m-d') }}</span>
<span class="awb-box">INVOICE NO: {{ $shipment->awb_no }}</span>
<span>INVOICE NO: {{ $shipment->awb_no }}</span>
</div>
</div>
<div class="two-col">
<div class="col section">
<div class="section-title">SHIPPER / فرستنده</div>
<div class="section-title">SHIPPER</div>
<div class="row"><span class="label">Name:</span><span class="value">{{ $shipper['name'] }}</span></div>
<div class="row"><span class="label">Company:</span><span class="value">{{ $shipper['company'] }}</span></div>
<div class="row"><span class="label">Contact:</span><span class="value">{{ $shipper['phone'] }}</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">City/Zip:</span><span class="value">{{ $shipper['city'] }} {{ $shipper['zip'] }}</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="col section">
<div class="section-title">CONSIGNEE / گیرنده</div>
<div class="section-title">CONSIGNEE</div>
<div class="row"><span class="label">Name:</span><span class="value">{{ $receiver['name'] }}</span></div>
<div class="row"><span class="label">Company:</span><span class="value">{{ $receiver['company'] }}</span></div>
<div class="row"><span class="label">Contact:</span><span class="value">{{ $receiver['phone'] }}</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">City/Zip:</span><span class="value">{{ $receiver['city'] }} {{ $receiver['zip'] }}</span></div>
<div class="row"><span class="label">Zip Code & 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>
<div class="section">
<div class="section-title">SHIPMENT DETAILS</div>
<div class="row">
<div style="width: 50%">
<div class="row"><span class="label">Weight:</span><span class="value">{{ $shipment->weight }} kg</span></div>
<div class="row"><span class="label">Volumetric:</span><span class="value">{{ $shipment->volumetric_weight }} kg</span></div>
<div class="row"><span class="label">Dimensions:</span><span class="value">{{ $shipment->dimensions }}</span></div>
<div class="row"><span class="label">Service:</span><span class="value">{{ $shipment->type?->label() }}</span></div>
<div class="row"><span class="label">Direction:</span><span class="value">{{ $shipment->direction?->label() }}</span></div>
</div>
<div style="width: 50%">
<div class="row"><span class="label">From:</span><span class="value">{{ $shipment->fromCountry?->name }}</span></div>
<div class="row"><span class="label">To:</span><span class="value">{{ $shipment->toCountry?->name }}</span></div>
<div class="row"><span class="label">Forwarder:</span><span class="value">{{ $shipment->forwarder }}</span></div>
<div class="row"><span class="label">Reason:</span><span class="value">{{ $shipment->reason_for_export ?: 'N/A' }}</span></div>
</div>
</div>
<div class="content-box">
<div class="content-title">CONTENT</div>
<p>{{ $shipment->content_description ?: 'General Goods' }}</p>
</div>
@if($shipment->isParcel() && $items->count() > 0)
<div class="section">
<div class="section-title">CONTENT / محموله</div>
<p style="margin-bottom: 10px;">{{ $shipment->reason_for_export ?: 'General Goods' }}</p>
<div class="section-title">SHIPMENT ITEMS</div>
<table>
<thead>
<tr>
<th style="width: 40px">No</th>
<th>Description</th>
<th style="width: 100px">H.S. Code</th>
<th style="width: 70px">Qty</th>
<th style="width: 90px">Unit Price (USD)</th>
<th style="width: 100px">Total (USD)</th>
<th style="width: 40px;">NO</th>
<th>DESCRIPTION</th>
<th style="width: 100px;">H.S. CODE</th>
<th style="width: 70px;">QUANTITY</th>
<th style="width: 90px;">UNIT PRICE (USD)</th>
<th style="width: 100px;">TOTAL IN USD</th>
</tr>
</thead>
<tbody>
@forelse($items as $item)
@foreach($items as $item)
<tr>
<td class="text-center">{{ $item->row_number }}</td>
<td style="text-align: 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-center">{{ number_format($item->unit_price, 2) }}</td>
<td class="text-center">{{ number_format($item->total_usd, 2) }}</td>
<td style="text-align: center;">{{ $item->hs_code }}</td>
<td style="text-align: center;">{{ $item->quantity }}</td>
<td style="text-align: center;">{{ number_format($item->unit_price, 2) }}</td>
<td style="text-align: center;">{{ number_format($item->total_usd, 2) }}</td>
</tr>
@empty
<tr><td colspan="6" class="text-center">No items</td></tr>
@endforelse
@endforeach
</tbody>
</table>
<div style="margin-top: 10px; text-align: left;">
<strong>TOTAL INVOICE AMOUNT IN USD: {{ number_format($invoice_total_usd, 2) }}</strong>
<div class="total-row">
TOTAL INVOICE AMOUNT IN USD: {{ number_format($invoice_total_usd, 2) }}
</div>
</div>
@endif
<div class="info-row">
<div class="info-box">
<div class="row"><span class="label">WEIGHT:</span><span class="value">{{ $shipment->weight }} KG</span></div>
<div class="row"><span class="label">DIMENSION:</span><span class="value">{{ $shipment->dimensions }} CM</span></div>
</div>
<div class="info-box">
<div class="row"><span class="label">REASON FOR EXPORT:</span><span class="value">{{ $shipment->reason_for_export ?: 'N/A' }}</span></div>
</div>
</div>
<div class="declaration">
I HEREBY STATE THAT THE ABOVE INFORMATION IS TRUE AND CORRECT TO THE BEST OF MY KNOWLEDGE.
I HEREBY STATE THAT THE ABOVE INFORMATION IS TRUE AND I, THE UNDERSIGNED, AM LIABLE FOR ANY CONSEQUENCES THAT MAY ARISE DUE TO NON-DISCLOSURE OF FACTS
</div>
<div class="signature-line">
<div class="signature-box">
NAME - SIGNATURE
</div>
<div class="signature-box">
NAME - SIGNATURE
</div>
</div>
<div class="label-section">
<div class="label-title">LABEL</div>
<div class="label-row">
<span><strong>AWB:</strong> {{ $shipment->awb_no }}</span>
<span><strong>Weight:</strong> {{ $shipment->weight }} KG</span>
</div>
<div class="label-row">
<span><strong>Gross Weight:</strong> {{ $shipment->weight }} KG</span>
<span><strong>Volumetric Weight:</strong> {{ $shipment->volumetric_weight }} KG</span>
</div>
<div class="label-row">
<span><strong>W*L*H:</strong> {{ $shipment->dimensions }}</span>
</div>
<div class="barcode">
{!! $barcode !!}
</div>
</div>
<div class="footer">
IFNEX Logistics We Deliver Value | AWB: {{ $shipment->awb_no }} | Generated on {{ now()->format('Y-m-d H:i') }}
IFNEX Logistics We Deliver Value | Generated on {{ now()->format('Y-m-d H:i') }}
</div>
</div>
</body>

View File

@ -1,75 +1,49 @@
<!DOCTYPE html>
<!--
LABEL PDF TEMPLATE TODO: تطبیق نهایی با قالب اکسل
Excel Reference: شیت «label» در فایل 01KYWGVNKS5TNMN37RV77PCYNZ.xlsx
Cell Mapping:
- C1: AWB number echo
- C3: AWB Number (lookup key)
- J2: Weight in KG
- J3: Weight echo
- A2:B2: Gross Weight
- A4: W*L*H formula
- J4: Origin Country
- J5: Destination Country
- J7: Destination Country Code
- A7:B7: Volumetric Weight
VLOOKUP Source: شیت «List»
Required Fields:
- AWB Number ( prominently displayed )
- Shipper: Name, Company, Phone, Address
- Receiver: Name, Company, Phone, Address
- Shipment: Gross Weight, Volumetric Weight, Dimensions, From/To Country
Current Implementation: A4 size for laser printer + sticker paper
Original Template: Compact thermal label format
TODO: اگر اندازه لیبل کوچک‌تر (مثل 100x150mm) نیاز است، صفحه‌بندی را تغییر دهید.
TODO: فونت و اندازه متن را طبق پرینتر لیبل‌زن تنظیم کنید.
TODO: در صورت نیاز به بارکد/QR، کتابخانه بارکد اضافه کنید.
Font: باید فونت رسمی IFNEX استفاده شود
Colors: لوگو نارنجی #f37021
Page: فعلی A4 Portrait قابل تغییر به اندازه سفارشی
-->
<html lang="fa" dir="rtl">
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: DejaVu Sans, sans-serif; font-size: 12px; direction: rtl; text-align: right; }
.page { width: 210mm; min-height: 297mm; padding: 10mm; margin: 0 auto; }
body { font-family: DejaVu Sans, sans-serif; font-size: 12px; direction: ltr; text-align: left; color: #333; }
.page { width: 190mm; min-height: 277mm; padding: 10mm; margin: 0 auto; }
.label-box {
border: 2px solid #333;
border: 3px solid #000;
padding: 15px;
margin-bottom: 20px;
page-break-inside: avoid;
margin: 0 auto;
}
.header { display: flex; align-items: center; border-bottom: 2px solid #f37021; padding-bottom: 10px; margin-bottom: 15px; }
.logo { width: 50px; height: auto; margin-left: 10px; }
.company-info { flex: 1; }
.company-name { font-size: 16px; font-weight: bold; color: #f37021; }
.awb-display { font-size: 18px; font-weight: bold; background: #f37021; color: white; padding: 5px 15px; display: inline-block; margin-top: 5px; }
.logo { width: 70px; height: auto; }
.company-name { font-size: 18px; font-weight: bold; color: #f37021; margin-left: 10px; }
.grid { display: flex; flex-wrap: wrap; gap: 15px; }
.col { flex: 1; min-width: 45%; }
.awb-badge {
background-color: #f37021;
color: #fff;
font-size: 22px;
font-weight: bold;
text-align: center;
padding: 8px;
margin-bottom: 15px;
letter-spacing: 3px;
}
.section-title { font-weight: bold; color: #f37021; font-size: 11px; margin-bottom: 8px; text-transform: uppercase; }
.barcode { text-align: center; margin-bottom: 15px; }
.row { margin-bottom: 5px; }
.grid { display: flex; gap: 15px; margin-bottom: 15px; }
.col { flex: 1; }
.section-title { font-weight: bold; color: #f37021; font-size: 12px; margin-bottom: 8px; text-transform: uppercase; border-bottom: 1px solid #eee; padding-bottom: 3px; }
.row { margin-bottom: 4px; }
.label { font-weight: bold; color: #555; display: inline-block; width: 100px; }
.value { display: inline-block; }
.shipment-info { background-color: #f9f9f9; padding: 10px; border: 1px solid #ddd; margin-top: 15px; }
.shipment-grid { display: flex; flex-wrap: wrap; gap: 10px; }
.shipment-item { flex: 1; min-width: 45%; }
.shipment-info { background-color: #f9f9f9; padding: 10px; border: 1px solid #ddd; margin-bottom: 15px; }
.shipment-grid { display: flex; gap: 15px; }
.shipment-item { flex: 1; }
.footer { margin-top: 15px; text-align: center; font-size: 10px; color: #666; border-top: 1px solid #ddd; padding-top: 10px; }
.footer { text-align: center; font-size: 10px; color: #666; border-top: 1px solid #ddd; padding-top: 10px; }
</style>
</head>
<body>
@ -77,22 +51,27 @@
<div class="label-box">
<div class="header">
<img src="{{ public_path('logo.png') }}" class="logo" alt="IFNEX Logo">
<div class="company-info">
<div class="company-name">IFNEX LOGISTICS</div>
<div class="awb-display">{{ $shipment->awb_no }}</div>
</div>
<div class="awb-badge">
{{ $shipment->awb_no }}
</div>
<div class="barcode">
{!! $barcode !!}
</div>
<div class="grid">
<div class="col">
<div class="section-title">SHIPPER / فرستنده</div>
<div class="section-title">SHIPPER</div>
<div class="row"><span class="label">Name:</span><span class="value">{{ $shipper['name'] }}</span></div>
<div class="row"><span class="label">Company:</span><span class="value">{{ $shipper['company'] }}</span></div>
<div class="row"><span class="label">Phone:</span><span class="value">{{ $shipper['phone'] }}</span></div>
<div class="row"><span class="label">Address:</span><span class="value">{{ $shipper['address'] }}</span></div>
</div>
<div class="col">
<div class="section-title">RECEIVER / گیرنده</div>
<div class="section-title">RECEIVER</div>
<div class="row"><span class="label">Name:</span><span class="value">{{ $receiver['name'] }}</span></div>
<div class="row"><span class="label">Company:</span><span class="value">{{ $receiver['company'] }}</span></div>
<div class="row"><span class="label">Phone:</span><span class="value">{{ $receiver['phone'] }}</span></div>
@ -104,9 +83,9 @@
<div class="section-title">SHIPMENT DETAILS</div>
<div class="shipment-grid">
<div class="shipment-item">
<div class="row"><span class="label">Gross Weight:</span><span class="value">{{ $shipment->weight }} kg</span></div>
<div class="row"><span class="label">Volumetric:</span><span class="value">{{ $shipment->volumetric_weight }} kg</span></div>
<div class="row"><span class="label">Dimensions:</span><span class="value">{{ $shipment->dimensions }}</span></div>
<div class="row"><span class="label">Gross Weight:</span><span class="value">{{ $shipment->weight }} KG</span></div>
<div class="row"><span class="label">Volumetric Weight:</span><span class="value">{{ $shipment->volumetric_weight }} KG</span></div>
<div class="row"><span class="label">Dimensions:</span><span class="value">{{ $shipment->dimensions }} CM</span></div>
</div>
<div class="shipment-item">
<div class="row"><span class="label">From:</span><span class="value">{{ $shipment->fromCountry?->name }}</span></div>
@ -117,7 +96,7 @@
</div>
<div class="footer">
IFNEX Logistics | We Deliver Value | {{ $shipment->awb_no }}
IFNEX Logistics We Deliver Value
</div>
</div>
</div>