Files
core/src/Shipping/Services/ShipmentTrackingRecorder.php
T

81 lines
2.7 KiB
PHP

<?php
namespace Modules\Core\Shipping\Services;
use Modules\Core\Shipping\Contracts\CarrierFulfillmentInterface;
use Modules\Core\Shipping\Contracts\SupportsTracking;
use Modules\Core\Shipping\DTOs\TrackingCheckpoint;
use Modules\Core\Shipping\Events\ShipmentStatusUpdatedByCarrier;
use Modules\Core\Shipping\Models\Shipment;
use Modules\Core\Shipping\Models\ShipmentInfo;
/**
* The one place carrier checkpoints become ShipmentInfo rows (the carrier
* info every screen reads): new checkpoints only — matched on status +
* time — each dispatching ShipmentStatusUpdatedByCarrier, so order status,
* emails and the order Timeline follow. Used by PollShipmentTrackingJob,
* the voucher sync and the "Refresh tracking" actions.
*/
class ShipmentTrackingRecorder
{
/**
* Fetches the shipment's history from its carrier and records what's
* new. Returns how many checkpoints were added (0 when the carrier has
* no tracking API).
*/
public function refresh(Shipment $shipment): int
{
$service = app(CarrierFulfillmentInterface::class, ['carrier' => $shipment->carrier]);
if (! $service instanceof SupportsTracking || blank($shipment->tracking_reference)) {
return 0;
}
return $this->record($shipment, $service->trackShipment($shipment));
}
/**
* @param iterable<TrackingCheckpoint> $checkpoints
*/
public function record(Shipment $shipment, iterable $checkpoints): int
{
$existing = $shipment->shipmentInfo()
->get(['status', 'occurred_at'])
->map(fn (ShipmentInfo $info) => $this->fingerprint($info->status->value, $info->occurred_at->toIso8601String()))
->flip();
$added = 0;
foreach ($checkpoints as $checkpoint) {
$fingerprint = $this->fingerprint($checkpoint->status->value, $checkpoint->occurredAt->toIso8601String());
if ($existing->has($fingerprint)) {
continue;
}
$existing->put($fingerprint, true);
$info = ShipmentInfo::create([
'shipment_id' => $shipment->id,
'status' => $checkpoint->status,
'carrier_status' => $checkpoint->carrierStatus,
'message' => $checkpoint->message,
'location' => $checkpoint->location,
'occurred_at' => $checkpoint->occurredAt,
'meta' => $checkpoint->meta,
]);
ShipmentStatusUpdatedByCarrier::dispatch($info);
$added++;
}
return $added;
}
private function fingerprint(string $status, string $occurredAt): string
{
return $status.'|'.$occurredAt;
}
}