ifnex/04_Laravel/app/Imports/ShippingRatesImport.php
Kazem Alghasi abae820070 refactor(api): improve pricing logic and add test coverage
Refactor the pricing calculation service and shipping rate import logic to improve consistency and reliability. This includes updating direction naming conventions, enhancing model casting, and adding comprehensive test suites.

- Update `ShippingRatesImport` to use 'outbound' and 'inbound' instead of 'export' and 'import'
- Refactor `PriceCalculatorService` to use a more modular calculation structure
- Update `ShippingRate` model to use explicit property casting for zones
- Add `HasFactory` trait to `Country` and `ShippingRate` models
- Add new database factories for `Country` and `ShippingRate`
- Implement new feature tests for API endpoints and service logic
- Add new service tests for `PriceCalculatorService`
2026-08-07 04:39:14 +03:30

213 lines
6.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Imports;
use App\Enums\ShipmentType;
use App\Models\ShippingRate;
use App\Models\SystemSetting;
use Maatwebsite\Excel\Concerns\OnEachRow;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
use Maatwebsite\Excel\Concerns\WithStartRow;
use Maatwebsite\Excel\Row;
class ShippingRatesImport implements WithMultipleSheets
{
/**
* تعریف شیت‌های مختلف فایل اکسل و کلاس‌های پردازش‌گر مربوطه
*/
public function sheets(): array
{
return [
'Export Rate' => new RateSheetImport('outbound'), // تغییر به حروف کوچک
'Import Rate' => new RateSheetImport('inbound'), // تغییر به حروف کوچک
'DocEco' => new SimpleRateSheetImport(ShipmentType::DocEconomy, 'outbound'), // تغییر به حروف کوچک
];
}
}
/**
* کلاس برای پردازش شیت‌های استاندارد نرخ‌ها (Export Rate و Import Rate)
*/
class RateSheetImport implements OnEachRow, WithStartRow
{
protected int $imported = 0;
protected int $skipped = 0;
protected array $errors = [];
public function __construct(protected string $direction) {}
/**
* شروع خواندن از ردیف ۵ (۴ ردیف اول هدر هستند)
*/
public function startRow(): int
{
return 5;
}
/**
* پردازش هر ردیف از شیت
*/
public function onRow(Row $row)
{
$rowIndex = $row->getIndex();
$cells = $row->toArray();
// بررسی اعتبار ستون وزن (ستون اول)
if (empty($cells[0]) || !is_numeric($cells[0])) {
$this->skipped++;
return;
}
try {
// تشخیص نوع سرویس (Doc یا Parcel)
$type = $this->resolveType($rowIndex, $cells);
// استخراج و نرمال‌سازی قیمت‌های ۱۰ زون
$zones = [];
for ($i = 1; $i <= 10; $i++) {
$val = str_replace([',', ' '], '', (string) ($cells[$i] ?? 0));
$zones['zone_' . $i] = is_numeric($val) ? (float) $val : 0;
}
// ثبت یا به‌روزرسانی نرخ در دیتابیس
ShippingRate::updateOrCreate(
[
'direction' => $this->direction, // اکنون مقدار صحیح (Outbound یا Inbound) است
'type' => $type->value,
'weight' => (float) $cells[0],
],
$zones
);
$this->imported++;
} catch (\Throwable $e) {
$this->errors[] = "Row $rowIndex: " . $e->getMessage();
$this->skipped++;
}
}
/**
* دریافت آمار عملیات ایمپورت
*/
public function getStats(): array
{
return [
'imported' => $this->imported,
'skipped' => $this->skipped,
'errors' => $this->errors,
];
}
/**
* تشخیص نوع سرویس بر اساس محتوای ردیف
*/
private function resolveType(int $rowIndex, array $cells): ShipmentType
{
$header = strtoupper((string) ($cells[3] ?? ''));
if (str_contains($header, 'DOCUMENT') || str_contains($header, 'DOC')) {
return ShipmentType::DocNormal;
}
if (str_contains($header, 'NON') || str_contains($header, 'PARCEL')) {
return ShipmentType::Parcel;
}
// اگر نوع مشخص نبود، بر اساس شماره ردیف حدس می‌زنیم
return $rowIndex < 10 ? ShipmentType::DocNormal : ShipmentType::Parcel;
}
}
/**
* کلاس برای پردازش شیت ساده‌تر نرخ‌ها (DocEco)
*/
class SimpleRateSheetImport implements OnEachRow, WithStartRow
{
protected int $imported = 0;
protected int $skipped = 0;
protected array $errors = [];
public function __construct(
protected ShipmentType $type,
protected string $direction = 'outbound' // تغییر پیش‌فرض به حروف کوچک
) {}
/**
* شروع خواندن از ردیف ۲ (۱ ردیف اول هدر است)
*/
public function startRow(): int
{
return 2;
}
/**
* پردازش هر ردیف از شیت
*/
public function onRow(Row $row)
{
$cells = $row->toArray();
// بررسی اعتبار ستون وزن (ستون اول)
if (empty($cells[0]) || !is_numeric($cells[0])) {
$this->skipped++;
return;
}
try {
$weight = (float) $cells[0];
$zoneNumber = (int) $cells[1];
$rawValue = str_replace([',', ' '], '', (string) ($cells[2] ?? 0));
$rawValue = is_numeric($rawValue) ? (float) $rawValue : 0;
// بررسی اعتبار شماره زون و قیمت
if ($zoneNumber < 1 || $zoneNumber > 10 || $rawValue <= 0) {
$this->skipped++;
return;
}
// تبدیل قیمت از AED به IRR
$aedToIrr = (float) SystemSetting::get('aed_to_irr', 455000);
$value = $aedToIrr > 0 ? $rawValue / $aedToIrr : $rawValue;
$zoneColumn = 'zone_' . $zoneNumber;
// ایجاد مقادیر پیش‌فرض برای زون‌ها
$defaults = [];
for ($i = 1; $i <= 10; $i++) {
$defaults['zone_' . $i] = 0;
}
// یافتن یا ایجاد رکورد و به‌روزرسانی قیمت زون
$record = ShippingRate::firstOrCreate(
[
'direction' => $this->direction, // اکنون مقدار صحیح (Outbound یا Inbound) است
'type' => $this->type->value,
'weight' => $weight,
],
$defaults
);
$record->{$zoneColumn} = $value;
$record->save();
$this->imported++;
} catch (\Throwable $e) {
$this->errors[] = "Row " . $row->getIndex() . ": " . $e->getMessage();
$this->skipped++;
}
}
/**
* دریافت آمار عملیات ایمپورت
*/
public function getStats(): array
{
return [
'imported' => $this->imported,
'skipped' => $this->skipped,
'errors' => $this->errors,
];
}
}