ifnex/04_Laravel/app/Services/PriceCalculatorService.php
Kazem Alghasi 3aaa9a5481 refactor(api): improve shipping rate import and pricing logic
Refactor the shipping rate import process to support bidirectional
data handling (import/export) and implement currency conversion using
system settings. Update the price calculator to ensure valid zone
data is retrieved.

- Update `ShippingRatesImport` to handle both import and export
  directions via `SimpleRateSheetImport`.
- Implement AED to IRR conversion during rate importation using
  `SystemSetting`.
- Add validation for zone numbers and rate values during import.
- Enhance `PriceCalculatorService` to filter for non-zero zone values.
- Add various debug and testing scripts for pricing and data
  structure verification.
2026-08-04 07:24:23 +03:30

85 lines
3.2 KiB
PHP

<?php
namespace App\Services;
use App\Enums\ShipmentType;
use App\Models\Country;
use App\Models\Shipment;
use App\Models\SystemSetting;
use InvalidArgumentException;
class PriceCalculatorService
{
public function calculate(array $data): array
{
$type = ShipmentType::from($data['type']);
$direction = $data['direction'] === 'Outbound' ? 'export' : 'import';
$chargeableWeight = max($data['weight'], $data['volumetric_weight']);
$country = Country::where('iso_code', $data['country_iso'])->firstOrFail();
$zone = match (true) {
$type === ShipmentType::Parcel && $direction === 'export' => $country->export_zone_parcel,
$type === ShipmentType::Parcel && $direction === 'import' => $country->import_zone_parcel,
$type !== ShipmentType::Parcel && $direction === 'export' => $country->export_zone_doc,
$type !== ShipmentType::Parcel && $direction === 'import' => $country->import_zone_doc,
};
$rate = $this->lookupRate($direction, $type, $chargeableWeight, $zone);
$profitMargin = (float) SystemSetting::get('profit_margin', 1.25);
$aedToIrr = (float) SystemSetting::get('aed_to_irr', 455000);
$vatRate = (float) SystemSetting::get('vat_rate', 0.09);
$packingCostDefault = (float) SystemSetting::get('packing_cost_default', 100000);
$netDirham = $rate * $profitMargin;
$netRial = $netDirham * $aedToIrr;
$extraService = (float) ($data['extra_service'] ?? 0);
$packingCost = (float) ($data['packing_cost'] ?? $packingCostDefault);
$domesticPickup = (float) ($data['domestic_pickup'] ?? 0);
$domesticDelivery = (float) ($data['domestic_delivery'] ?? 0);
$warehousingCost = (float) ($data['warehousing_cost'] ?? 0);
$discount = (float) ($data['discount'] ?? 0);
$subtotal = $netRial + $extraService + $packingCost + $domesticPickup + $domesticDelivery + $warehousingCost - $discount;
$totalFee = $subtotal * (1 + $vatRate);
$vatAmount = $totalFee - $subtotal;
return [
'base_price' => $rate,
'net_dirham' => $netDirham,
'net_rial' => $netRial,
'extra_service' => $extraService,
'packing_cost' => $packingCost,
'domestic_pickup' => $domesticPickup,
'domestic_delivery' => $domesticDelivery,
'warehousing_cost' => $warehousingCost,
'discount' => $discount,
'vat_amount' => $vatAmount,
'total_fee' => $totalFee,
'zone' => $zone,
'chargeable_weight' => $chargeableWeight,
];
}
private function lookupRate(string $direction, ShipmentType $type, float $weight, int $zone): float
{
$zoneColumn = 'zone_' . $zone;
$rate = \App\Models\ShippingRate::query()
->where('direction', $direction)
->where('type', $type->value)
->where('weight', '<=', $weight)
->where($zoneColumn, '>', 0)
->orderByDesc('weight')
->value($zoneColumn);
if ($rate === null) {
throw new InvalidArgumentException('No rate found for the given parameters.');
}
return (float) $rate;
}
}