ifnex/04_Laravel/app/Http/Controllers/ShipmentPdfController.php
Kazem Alghasi 8cf407553b feat(security): implement secure file storage and shipment access control
Introduce a 'secure' filesystem disk to prevent public access to sensitive
commitment form uploads. Files are now stored in a non-public directory
and served via a protected controller method that validates user ownership.

Additionally, implement shipment authorization policies to ensure users
can only access PDF documents (AWB, invoice, labels) belonging to their
own orders.

Other changes:
- Add production environment check for Zarinpal gateway configuration
  to prevent accidental use of sandbox credentials.
- Move discount code and commitment form routes under authentication
  middleware for improved security.
- Add `ShipmentPolicy` to handle resource authorization.
2026-10-01 03:34:28 +03:30

189 lines
6.6 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Enums\ShipmentType;
use App\Models\Shipment;
use App\Services\PdfService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class ShipmentPdfController extends Controller
{
public function __construct(protected PdfService $pdf) {}
/**
* تولید AWB PDF (برای همه نوع محموله‌ها)
*/
public function awb(Shipment $shipment)
{
$this->authorize('view', $shipment);
try {
Log::info('Generating AWB PDF', ['shipment_id' => $shipment->id]);
$content = $this->pdf->awb($shipment);
Log::info('AWB PDF generated successfully', ['awb' => $shipment->awb_no]);
return response($content, 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'attachment; filename="AWB-' . $shipment->awb_no . '.pdf"',
]);
} catch (\Throwable $e) {
Log::error('AWB PDF generation failed', [
'shipment_id' => $shipment->id,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
return response('PDF generation failed: ' . $e->getMessage(), 500);
}
}
/**
* تولید Invoice PDF — فقط برای محموله‌های دارای کالا (PARCEL)
*
* محموله‌های DOC (DOC_NORMAL/DOC_ECONOMY) که کالا ندارن،
* فاکتور صادراتی ندارن و این متد براشون ارور برمی‌گردونه.
*/
public function invoice(Shipment $shipment)
{
$this->authorize('view', $shipment);
try {
// بارگذاری items برای بررسی
$shipment->loadMissing(['items']);
// بررسی نوع محموله
$isDocType = in_array($shipment->type, [
ShipmentType::DocNormal,
ShipmentType::DocEconomy,
]);
if ($isDocType && $shipment->items->isEmpty()) {
Log::info('Invoice PDF not available for DOC shipment without items', [
'shipment_id' => $shipment->id,
'type' => $shipment->type?->value,
]);
return response()->json([
'success' => false,
'message' => 'فاکتور فقط برای محموله‌های دارای کالا (PARCEL) صادر می‌شود.',
'hint' => 'محموله‌های DOC (مدارک) فاکتور صادراتی ندارند.',
], 400);
}
$content = $this->pdf->invoice($shipment);
Log::info('Invoice PDF generated successfully', [
'awb' => $shipment->awb_no,
'type' => $shipment->type?->value,
'items_count' => $shipment->items->count(),
]);
return response($content, 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'attachment; filename="INVOICE-' . $shipment->awb_no . '.pdf"',
]);
} catch (\InvalidArgumentException $e) {
// خطای مربوط به نوع محموله
Log::info('Invoice PDF skipped', [
'shipment_id' => $shipment->id,
'reason' => $e->getMessage(),
]);
return response()->json([
'success' => false,
'message' => $e->getMessage(),
], 400);
} catch (\Throwable $e) {
Log::error('Invoice PDF generation failed', [
'shipment_id' => $shipment->id,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
return response('PDF generation failed: ' . $e->getMessage(), 500);
}
}
/**
* تولید Label PDF — اندازه استاندارد لیبل پستی (100x150mm)
*/
public function label(Shipment $shipment)
{
$this->authorize('view', $shipment);
try {
$content = $this->pdf->label($shipment);
Log::info('Label PDF generated successfully', ['awb' => $shipment->awb_no]);
return response($content, 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'attachment; filename="LABEL-' . $shipment->awb_no . '.pdf"',
]);
} catch (\Throwable $e) {
Log::error('Label PDF generation failed', [
'shipment_id' => $shipment->id,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
return response('PDF generation failed: ' . $e->getMessage(), 500);
}
}
/**
* تولید PDF فاکتور فروش خدمات (واردات) — مطابق Sheet ENG Invoice
*/
public function importInvoice(Shipment $shipment, Request $request)
{
$this->authorize('view', $shipment);
try {
// دریافت پارامترها از درخواست
$options = $request->only([
'invoice_no',
'invoice_date',
'customs_office',
'exchange_rate',
'international_freight',
'pick_up_fee',
'brand_fee',
'report_fee',
'other_charges',
'customs_clearance',
'domestic_transport',
'warehousing_fee',
'order_registration_fee',
'other_clearance_charges',
'terms_and_conditions',
]);
// تبدیل exchange_rate به عدد
if (isset($options['exchange_rate'])) {
$options['exchange_rate'] = (float) $options['exchange_rate'];
}
$content = $this->pdf->importInvoice($shipment, $options);
Log::info('Import Invoice PDF generated successfully', [
'awb' => $shipment->awb_no,
'options' => $options,
]);
return response($content, 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'attachment; filename="SERVICE-INVOICE-' . $shipment->awb_no . '.pdf"',
]);
} catch (\Throwable $e) {
Log::error('Import Invoice PDF generation failed', [
'shipment_id' => $shipment->id,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
return response('PDF generation failed: ' . $e->getMessage(), 500);
}
}
}