loadMissing(['fromCountry', 'toCountry', 'items']); $data = [ '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, 'barcode_base64' => $this->generateBarcode($shipment->awb_no), 'service_label' => $this->getServiceLabel($shipment), 'type_label' => $this->getTypeLabel($shipment), ]; $html = view('pdfs.awb', $data)->render(); return $this->generatePdf($html, 'A4', 'portrait'); } /** * تولید Invoice PDF — فقط برای PARCEL و DOC_NORMAL/DOC_ECONOMY * (نه برای محموله‌های DOC خالص که کالا ندارن) */ public function invoice(Shipment $shipment): string { // اگه محموله DOC هست و آیتم نداره، invoice تولید نکن if ($shipment->type === ShipmentType::DocNormal || $shipment->type === ShipmentType::DocEconomy) { if ($shipment->items->isEmpty()) { throw new \InvalidArgumentException( 'فاکتور فقط برای محموله‌های دارای کالا (PARCEL) صادر می‌شود. محموله‌های DOC بدون کالا فاکتور ندارند.' ); } } $shipment->loadMissing(['fromCountry', 'toCountry', 'items']); $data = [ '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, 'barcode_base64' => $this->generateBarcode($shipment->awb_no), ]; $html = view('pdfs.invoice', $data)->render(); return $this->generatePdf($html, 'A4', 'portrait'); } /** * تولید Label PDF — اندازه استاندارد لیبل پستی (100x150mm) */ public function label(Shipment $shipment): string { $shipment->loadMissing(['fromCountry', 'toCountry']); $data = [ 'shipment' => $shipment, 'barcode_base64' => $this->generateBarcode($shipment->awb_no), 'origin_iso' => $shipment->fromCountry?->iso_code ?? 'IR', 'dest_iso' => $shipment->toCountry?->iso_code ?? '', ]; $html = view('pdfs.label', $data)->render(); // A5 افقی (Landscape) — استاندارد لیبل پستی: 210×148 mm return $this->generatePdf($html, 'A5', 'landscape'); } /** * تولید PDF فاکتور فروش خدمات (واردات) — مطابق Sheet ENG Invoice */ public function importInvoice(Shipment $shipment, array $options = []): string { $shipment->loadMissing(['fromCountry', 'toCountry', 'items']); // آدرس گیرنده $receiver = [ 'name' => $shipment->receiver_name, 'company' => $shipment->receiver_company, 'phone' => $shipment->receiver_phone, 'email' => $shipment->receiver_email, 'address' => $shipment->receiver_address, 'city' => $shipment->receiver_city, 'country' => $shipment->toCountry?->name ?? '', ]; // شماره فاکتور (از شماره AWB مشتق می‌شود) $invoice_no = $options['invoice_no'] ?? 'PEX' . date('ymd') . substr($shipment->awb_no, -4); // تاریخ فاکتور $invoice_date = $options['invoice_date'] ?? now()->format('l, F j, Y'); // دفتر گمرکی $customs_office = $options['customs_office'] ?? 'Imam Khomeini Airport'; // نرخ ارز $exchange_rate = $options['exchange_rate'] ?? 500000; // پیش‌فرض // اقلام خدماتی (۱۰ ردیف ثابت مطابق Sheet ENG Invoice) $service_items = $this->buildServiceItems($shipment, $options); // جمع‌بندی $freight_items = array_slice($service_items, 0, 5); // آیتم‌های حمل $clearance_items = array_slice($service_items, 5); // آیتم‌های ترخیص $freight_subtotal_currency = array_sum(array_column($freight_items, 'total_currency')); $freight_subtotal_rial = array_sum(array_column($freight_items, 'total_rial')); $clearance_subtotal_currency = array_sum(array_column($clearance_items, 'total_currency')); $clearance_subtotal_rial = array_sum(array_column($clearance_items, 'total_rial')); $total_currency = $freight_subtotal_currency + $clearance_subtotal_currency; $total_rial = $freight_subtotal_rial + $clearance_subtotal_rial; // شرایط و ضوابط $terms_and_conditions = $options['terms_and_conditions'] ?? 'This proforma invoice is issued based on the shipment weight declared by the customer. Final weight and dimensions will be determined after re-weighing at the destination customs.'; $data = [ 'shipment' => $shipment, 'receiver' => $receiver, 'invoice_no' => $invoice_no, 'invoice_date' => $invoice_date, 'customs_office' => $customs_office, 'exchange_rate' => $exchange_rate, 'service_items' => $service_items, 'freight_subtotal_currency' => $freight_subtotal_currency, 'freight_subtotal_rial' => $freight_subtotal_rial, 'clearance_subtotal_currency' => $clearance_subtotal_currency, 'clearance_subtotal_rial' => $clearance_subtotal_rial, 'total_currency' => $total_currency, 'total_rial' => $total_rial, 'terms_and_conditions' => $terms_and_conditions, ]; $html = view('pdfs.import_invoice', $data)->render(); return $this->generatePdf($html, 'A4', 'portrait'); } /** * ساخت اقلام خدماتی فاکتور واردات */ private function buildServiceItems(Shipment $shipment, array $options): array { // فیلدهای پیش‌فرض از گزینه‌ها یا مقادیر پیش‌فرض $fields = [ 'international_freight' => $options['international_freight'] ?? ($shipment->shipping_price ?? 0), 'pick_up_fee' => $options['pick_up_fee'] ?? ($shipment->domestic_pickup ?? 0), 'brand_fee' => $options['brand_fee'] ?? 0, 'report_fee' => $options['report_fee'] ?? 0, 'other_charges' => $options['other_charges'] ?? ($shipment->extra_service ?? 0), 'customs_clearance' => $options['customs_clearance'] ?? 0, 'domestic_transport' => $options['domestic_transport'] ?? ($shipment->domestic_delivery ?? 0), 'warehousing_fee' => $options['warehousing_fee'] ?? ($shipment->warehousing_cost ?? 0), 'order_registration_fee' => $options['order_registration_fee'] ?? 0, 'other_clearance_charges' => $options['other_clearance_charges'] ?? 0, ]; $exchange_rate = $options['exchange_rate'] ?? 500000; $weight = $shipment->chargeable_weight ?? $shipment->weight; return [ // آیتم‌های حمل (Freight) [ 'description' => 'International Freight', 'unit' => 'kg', 'quantity' => $weight, 'unit_price' => $fields['international_freight'] > 0 ? $fields['international_freight'] / $weight : 0, 'total_currency' => $fields['international_freight'], 'total_rial' => $fields['international_freight'] * $exchange_rate, 'notes' => '', ], [ 'description' => 'Pick Up Fee', 'unit' => '', 'quantity' => 0, 'unit_price' => 0, 'total_currency' => $fields['pick_up_fee'], 'total_rial' => $fields['pick_up_fee'] * $exchange_rate, 'notes' => '', ], [ 'description' => 'Brand Fee', 'unit' => '', 'quantity' => 0, 'unit_price' => 0, 'total_currency' => $fields['brand_fee'], 'total_rial' => $fields['brand_fee'] * $exchange_rate, 'notes' => '', ], [ 'description' => 'Report Fee', 'unit' => '', 'quantity' => 0, 'unit_price' => 0, 'total_currency' => $fields['report_fee'], 'total_rial' => $fields['report_fee'] * $exchange_rate, 'notes' => '', ], [ 'description' => 'Other Charges', 'unit' => '', 'quantity' => 0, 'unit_price' => 0, 'total_currency' => $fields['other_charges'], 'total_rial' => $fields['other_charges'] * $exchange_rate, 'notes' => '', ], // آیتم‌های ترخیص (Clearance) [ 'description' => 'Customs Clearance', 'unit' => 'service', 'quantity' => $weight, 'unit_price' => $fields['customs_clearance'] > 0 ? $fields['customs_clearance'] / $weight : 0, 'total_currency' => $fields['customs_clearance'], 'total_rial' => $fields['customs_clearance'] * $exchange_rate, 'notes' => '', ], [ 'description' => 'Domestic Transport', 'unit' => '', 'quantity' => 0, 'unit_price' => 0, 'total_currency' => $fields['domestic_transport'], 'total_rial' => $fields['domestic_transport'] * $exchange_rate, 'notes' => '', ], [ 'description' => 'Warehousing Fee', 'unit' => '', 'quantity' => 0, 'unit_price' => 0, 'total_currency' => $fields['warehousing_fee'], 'total_rial' => $fields['warehousing_fee'] * $exchange_rate, 'notes' => '', ], [ 'description' => 'Order Registration Fee', 'unit' => '', 'quantity' => 0, 'unit_price' => 0, 'total_currency' => $fields['order_registration_fee'], 'total_rial' => $fields['order_registration_fee'] * $exchange_rate, 'notes' => '', ], [ 'description' => 'Other Clearance Charges', 'unit' => '', 'quantity' => 0, 'unit_price' => 0, 'total_currency' => $fields['other_clearance_charges'], 'total_rial' => $fields['other_clearance_charges'] * $exchange_rate, 'notes' => '', ], ]; } /** * تولید بارکد از AWB number — base64 embed (مطمئن‌ترین روش) */ private function generateBarcode(string $awbNo): string { try { $generator = new BarcodeGeneratorPNG(); $barcode = $generator->getBarcode($awbNo, $generator::TYPE_CODE_128, 2, 40); // تبدیل به base64 و استفاده از data URI return 'data:image/png;base64,' . base64_encode($barcode); } catch (\Throwable $e) { Log::error('Barcode generation failed', [ 'awb' => $awbNo, 'error' => $e->getMessage(), ]); return ''; } } /** * برچسب Service برای AWB (Outbound/Inbound) */ private function getServiceLabel(Shipment $shipment): string { return match ($shipment->direction?->value) { 'export' => 'Outbound', 'import' => 'Inbound', default => 'Outbound', }; } /** * برچسب Type برای AWB (NON DOC / DOC) */ private function getTypeLabel(Shipment $shipment): string { return match ($shipment->type) { ShipmentType::Parcel => 'NON DOC', ShipmentType::DocNormal => 'DOC NORMAL', ShipmentType::DocEconomy => 'DOC ECONOMY', default => 'NON DOC', }; } /** * تولید PDF از HTML */ private function generatePdf(string $html, array|string $paper, string $orientation): string { $options = new Options(); $options->set('isHtml5ParserEnabled', true); $options->set('isRemoteEnabled', true); // مهم: برای data URI $options->set('defaultFont', 'DejaVu Sans'); $options->set('dpi', 150); $options->set('debugKeepTemp', false); $options->set('debugCss', false); $options->set('debugLayout', false); $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(); } /** * فرمت‌بندی آدرس فرستنده/گیرنده */ 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'}, ]); } }