Feat: Shipping functionalities redesign, elta courier integration, commenting out acs since no integration can happen, flow updates
This commit is contained in:
@@ -7,9 +7,22 @@ use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Lunar\Models\Order;
|
||||
use Lunar\Shipping\Facades\Shipping;
|
||||
|
||||
class Shipment extends Model
|
||||
{
|
||||
/** Created from our admin through the carrier's API. */
|
||||
public const SOURCE_CREATED = 'created';
|
||||
|
||||
/** An integrated carrier's voucher whose number staff typed in (API down, or the courier's own voucher). */
|
||||
public const SOURCE_MANUAL_VOUCHER = 'manual_voucher';
|
||||
|
||||
/** A carrier with no integration (the `manual` shipping driver); tracked by hand. */
|
||||
public const SOURCE_MANUAL = 'manual';
|
||||
|
||||
/** Pulled from a carrier's own voucher list; may not be linked to an order yet. */
|
||||
public const SOURCE_SYNCED = 'synced';
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
protected $casts = [
|
||||
@@ -37,4 +50,77 @@ class Shipment extends Model
|
||||
{
|
||||
return $this->shipmentInfo()->latest('occurred_at')->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* The carrier's display name: a manual carrier's own name (snapshotted
|
||||
* from its shipping method), otherwise the shipping driver's name().
|
||||
*/
|
||||
public function carrierLabel(): string
|
||||
{
|
||||
if (filled($this->meta['carrier_name'] ?? null)) {
|
||||
return $this->meta['carrier_name'];
|
||||
}
|
||||
|
||||
$driver = collect(Shipping::getSupportedDrivers())->get($this->carrier);
|
||||
|
||||
return $driver?->name() ?? ucwords(str_replace('-', ' ', $this->carrier));
|
||||
}
|
||||
|
||||
/**
|
||||
* A link to the carrier's own tracking page — manual carriers only.
|
||||
* Integrated carriers' history is synced, so customers follow it on
|
||||
* our own order page instead.
|
||||
*/
|
||||
public function trackingUrl(): ?string
|
||||
{
|
||||
$template = $this->meta['tracking_url'] ?? null;
|
||||
|
||||
if ($this->source !== self::SOURCE_MANUAL || blank($template) || blank($this->tracking_reference)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return str_replace('{number}', rawurlencode($this->tracking_reference), $template);
|
||||
}
|
||||
|
||||
public function isCancelled(): bool
|
||||
{
|
||||
return $this->cancelled_at !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A return voucher (coming back to us), from a carrier's list. Its
|
||||
* checkpoints must never drive the order's own status.
|
||||
*/
|
||||
public function isReturn(): bool
|
||||
{
|
||||
return (bool) ($this->meta['is_return'] ?? false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this shipment's carrier checkpoints should move its order's
|
||||
* status (dispatched / delivered / delivery failed).
|
||||
*/
|
||||
public function drivesOrderStatus(): bool
|
||||
{
|
||||
return $this->order_id !== null && ! $this->isReturn() && ! $this->isCancelled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Only shipments created through the carrier's API have a label to
|
||||
* print — manual carriers, typed-in vouchers and synced ones don't.
|
||||
*/
|
||||
public function hasCarrierLabel(): bool
|
||||
{
|
||||
return $this->source === self::SOURCE_CREATED && ! $this->isCancelled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancelling a manual carrier's shipment or a typed-in voucher is a
|
||||
* local record change only — there's nothing to cancel at the carrier
|
||||
* through the API.
|
||||
*/
|
||||
public function cancelsLocallyOnly(): bool
|
||||
{
|
||||
return in_array($this->source, [self::SOURCE_MANUAL, self::SOURCE_MANUAL_VOUCHER, self::SOURCE_SYNCED], true);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user