ifnex/04_Laravel/app/Imports/TrackingDataImport.php
Kazem Alghasi f5917c34c2 feat(logic): integrate discount code application and wallet services
Implement the core logic for applying discount codes during price
calculation and introduce the WalletService. This includes updating
the PriceCalculatorService to validate and apply discounts,
incrementing usage counts, and adding a Filament resource for
managing discount codes via the admin panel.

Additionally, refactor TrackingDataImport to use OnEachRow for better
memory management during large Excel imports.
2026-08-05 19:16:09 +03:30

218 lines
6.9 KiB
PHP

<?php
namespace App\Imports;
use App\Models\Shipment;
use App\Models\ShipmentTrackingEvent;
use App\Models\Country;
use Illuminate\Support\Facades\Log;
use Maatwebsite\Excel\Concerns\OnEachRow;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Row;
class TrackingDataImport implements OnEachRow, WithHeadingRow
{
protected $importedEvents = 0;
protected $skippedEvents = 0;
protected $errors = [];
protected $createdShipments = 0;
public function onRow(Row $row)
{
$rowIndex = $row->getIndex();
$rowArray = $row->toArray();
try {
// استخراج و نرمال‌سازی داده‌ها از ردیف اکسل
$awbNo = $this->normalizeAwb($rowArray['awb'] ?? null);
$eventDate = $this->normalizeDate($rowArray['date'] ?? null);
$eventTime = $this->normalizeTime($rowArray['time'] ?? null);
$eventDescription = $this->normalizeDescription($rowArray['state'] ?? null);
$location = $this->normalizeLocation($rowArray['country'] ?? null);
$deliveryStatus = $this->normalizeDeliveryStatus($rowArray['last_state'] ?? null);
// بررسی اعتبار داده‌های ضروری
if (!$awbNo || !$eventDate || !$eventDescription) {
$this->skippedEvents++;
return;
}
// جستجوی مرسوله بر اساس AWB
$shipment = Shipment::where('awb_no', $awbNo)->first();
// اگر مرسوله وجود نداشت، آن را ایجاد کن
if (!$shipment) {
$shipment = $this->createShipmentFromTrackingRow($rowArray, $awbNo, $location);
$this->createdShipments++;
}
// ثبت رویداد ترکینگ
ShipmentTrackingEvent::create([
'shipment_id' => $shipment->id,
'event_date' => $eventDate,
'event_time' => $eventTime,
'location' => $location,
'event_description' => $eventDescription,
'delivery_status' => $deliveryStatus,
'source' => 'manual',
]);
$this->importedEvents++;
} catch (\Throwable $e) {
$this->skippedEvents++;
$this->errors[] = "Row {$rowIndex}: " . $e->getMessage();
Log::error('Tracking import error', ['row' => $rowIndex, 'error' => $e->getMessage()]);
}
}
protected function createShipmentFromTrackingRow(array $row, string $awbNo, ?string $location): Shipment
{
// استخراج نام کشور از لوکیشن
$countryName = $location ? explode(' - ', $location)[0] : null;
$country = Country::where('name', 'like', "%{$countryName}%")->first();
// تعیین وضعیت مرسوله بر اساس آخرین وضعیت
$lastState = strtolower($row['last_state'] ?? '');
$status = 'processed';
if (str_contains($lastState, 'delivered')) $status = 'delivered';
elseif (str_contains($lastState, 'in transit')) $status = 'in_transit';
elseif (str_contains($lastState, 'picked')) $status = 'picked_up';
elseif (str_contains($lastState, 'out for delivery')) $status = 'out_for_delivery';
elseif (str_contains($lastState, 'failed')) $status = 'failed';
// ایجاد مرسوله جدید با مقادیر پیش‌فرض
return Shipment::create([
'awb_no' => $awbNo,
'direction' => 'export',
'type' => 'PARCEL',
'status' => $status,
'weight' => 0,
'volumetric_weight' => 0,
'chargeable_weight' => 0,
'shipping_price' => 0,
'extra_service' => 0,
'packing_cost' => 0,
'domestic_pickup' => 0,
'domestic_delivery' => 0,
'warehousing_cost' => 0,
'vat_amount' => 0,
'discount' => 0,
'total_fee' => 0,
'net_dirham' => 0,
'net_rial' => 0,
'from_country_id' => Country::where('name', 'Iran')->first()?->id,
'to_country_id' => $country?->id,
'sender_name' => 'Unknown',
'receiver_name' => 'Unknown',
]);
}
// --- متدهای کمکی برای نرمال‌سازی داده‌ها ---
protected function normalizeAwb($value): ?string
{
if (!$value) return null;
$value = trim($value);
return $value === '' || $value === '0' ? null : $value;
}
protected function normalizeDate($value): ?string
{
if (!$value) return null;
if ($value instanceof \DateTime) {
return $value->format('Y-m-d');
}
$value = trim($value);
if ($value === '' || $value === '1899-12-31') return null;
try {
return date('Y-m-d', strtotime($value));
} catch (\Throwable $e) {
return null;
}
}
protected function normalizeTime($value): ?string
{
if (!$value) return null;
if ($value instanceof \DateTime) {
return $value->format('H:i:s');
}
$value = trim($value);
if ($value === '' || $value === '1899-12-31') return null;
try {
return date('H:i:s', strtotime($value));
} catch (\Throwable $e) {
return null;
}
}
protected function normalizeDescription($value): ?string
{
if (!$value) return null;
$value = trim($value);
return $value === '' ? null : $value;
}
protected function normalizeLocation($value): ?string
{
if (!$value) return null;
$value = trim($value);
return $value === '' || $value === '.' ? null : $value;
}
protected function normalizeDeliveryStatus($value): ?string
{
if (!$value) return null;
$value = trim($value);
if ($value === '') return null;
$statuses = [
'processed' => 'processed',
'picked up' => 'picked_up',
'in transit' => 'in_transit',
'out for delivery' => 'out_for_delivery',
'failed' => 'failed',
'delivered' => 'delivered',
'returned' => 'returned',
];
$lower = strtolower($value);
foreach ($statuses as $key => $status) {
if (str_contains($lower, $key)) {
return $status;
}
}
return null;
}
// --- متدهای دریافت آمار ---
public function getImportedEvents(): int
{
return $this->importedEvents;
}
public function getSkippedEvents(): int
{
return $this->skippedEvents;
}
public function getErrors(): array
{
return $this->errors;
}
public function getCreatedShipments(): int
{
return $this->createdShipments;
}
}