Files
core/src/Shipping/Carriers/Acs/AcsRateDriver.php
T

135 lines
4.3 KiB
PHP
Raw Normal View History

2026-07-19 00:50:51 +03:00
<?php
namespace Modules\Core\Shipping\Carriers\Acs;
use Lunar\DataTypes\Price;
use Lunar\DataTypes\ShippingOption;
use Lunar\Facades\Pricing;
use Lunar\Shipping\DataTransferObjects\ShippingOptionRequest;
use Lunar\Shipping\Interfaces\ShippingRateInterface;
use Lunar\Shipping\Models\ShippingRate;
use Modules\Core\Shipping\Carriers\Acs\Exceptions\AcsApiException;
use Modules\Core\Shipping\Contracts\SupportsLivePricing;
class AcsRateDriver implements ShippingRateInterface, SupportsLivePricing
{
public ShippingRate $shippingRate;
public function __construct(
private readonly AcsClient $client,
private readonly AreaResolver $areaResolver,
) {}
public function name(): string
{
return 'ACS Courier';
}
public function description(): string
{
return 'Live rate quote from ACS Courier.';
}
public function resolve(ShippingOptionRequest $shippingOptionRequest): ?ShippingOption
{
$shippingRate = $shippingOptionRequest->shippingRate;
$shippingMethod = $shippingRate->shippingMethod;
$cart = $shippingOptionRequest->cart;
$postcode = $cart->shippingAddress?->postcode;
if (! $postcode) {
return null;
}
if (($shippingRate->pricing_mode ?? 'live') === 'fixed') {
return $this->resolveFixedPrice($shippingRate, $shippingMethod, $cart);
}
return $this->resolveLivePrice($shippingRate, $shippingMethod, $cart, $postcode);
}
private function resolveFixedPrice(ShippingRate $shippingRate, $shippingMethod, $cart): ?ShippingOption
{
$subTotal = $cart->lines->sum('subTotal.value');
$pricing = Pricing::for($shippingRate)->qty($subTotal)->get();
if (! $pricing->matched) {
return null;
}
return new ShippingOption(
name: $shippingMethod->name ?: $this->name(),
description: $shippingMethod->description ?: $this->description(),
identifier: $shippingRate->getIdentifier(),
price: $pricing->matched->price,
taxClass: $shippingRate->getTaxClass(),
taxReference: $shippingRate->getTaxReference(),
);
}
private function resolveLivePrice(ShippingRate $shippingRate, $shippingMethod, $cart, string $postcode): ?ShippingOption
{
try {
$destination = $this->areaResolver->resolve($postcode);
$response = $this->client->call('ACS_Price_Calculation', [
'Billing_Code' => config('acs.billing_code'),
'Acs_Station_Destination' => $destination->stationId,
'Weight' => $this->totalWeightInKg($cart),
'Pickup_Date' => now()->toDateString(),
'Charge_Type' => 2,
])->throwIfError();
} catch (AcsApiException $e) {
report($e);
return null;
}
$amount = (int) round(($response->valueOutput['Total_Ammount'] ?? 0) * 100);
return new ShippingOption(
name: $shippingMethod->name ?: $this->name(),
description: $shippingMethod->description ?: $this->description(),
identifier: $shippingRate->getIdentifier(),
price: new Price($amount, $cart->currency, 1),
taxClass: $shippingRate->getTaxClass(),
taxReference: $shippingRate->getTaxReference(),
meta: ['acs_station_destination' => $destination->stationId],
);
}
public function on(ShippingRate $shippingRate): self
{
$this->shippingRate = $shippingRate;
return $this;
}
private function totalWeightInKg($cart): float
{
$weight = 0.0;
foreach ($cart->lines->load('purchasable') as $line) {
$variant = $line->purchasable;
if (! $variant || ! $variant->weight_value) {
continue;
}
$unit = $variant->weight_unit ?? 'kg';
$value = (float) $variant->weight_value;
$weight += match ($unit) {
'g' => $value / 1000,
'lb' => $value * 0.45359237,
'oz' => $value * 0.0283495231,
default => $value, // kg
} * $line->quantity;
}
return max($weight, 0.5); // ACS minimum billable weight
}
}