diff --git a/src/Order/Notifications/OrderDispatchedNotification.php b/src/Order/Notifications/OrderDispatchedNotification.php index e8c5fa8..a5019f1 100644 --- a/src/Order/Notifications/OrderDispatchedNotification.php +++ b/src/Order/Notifications/OrderDispatchedNotification.php @@ -57,7 +57,7 @@ class OrderDispatchedNotification extends BaseNotification 'reference' => $reference, // Active, numbered, non-return shipments: the voucher numbers // to show, plus the courier's link for manual carriers - // (Shipment::trackingUrl()). Integrated carriers' history is + // and typed-in vouchers (Shipment::trackingUrl()). API-created shipments' history is // on the customer's order page. 'shipments' => $order->shipments()->get() ->reject(fn (Shipment $shipment) => $shipment->isCancelled() || $shipment->isReturn() || blank($shipment->tracking_reference)) diff --git a/src/Order/Services/OrderFulfillmentService.php b/src/Order/Services/OrderFulfillmentService.php index a3315b7..8335c1f 100644 --- a/src/Order/Services/OrderFulfillmentService.php +++ b/src/Order/Services/OrderFulfillmentService.php @@ -118,6 +118,12 @@ class OrderFulfillmentService 'meta' => [ 'services' => array_map(fn (ExtraService $service) => $service->value, $services), 'cod_amount' => $this->flow->isCod($order) ? $order->total->decimal : null, + // Copied like ManualFulfillmentService does, so the link + // keeps working if the method is edited later. + 'tracking_url' => ShippingMethod::where('driver', $carrier)->get() + ->map(fn (ShippingMethod $method) => $method->data['tracking_url'] ?? null) + ->filter() + ->first(), ], ]); diff --git a/src/Shipping/Extensions/OrderShipmentsExtension.php b/src/Shipping/Extensions/OrderShipmentsExtension.php index 46af6f5..5063778 100644 --- a/src/Shipping/Extensions/OrderShipmentsExtension.php +++ b/src/Shipping/Extensions/OrderShipmentsExtension.php @@ -124,12 +124,13 @@ class OrderShipmentsExtension extends ViewPageExtension ->schema(fn (Shipment $record) => [ TextInput::make('tracking_reference') ->label('Voucher / tracking number') + ->required($record->source === Shipment::SOURCE_MANUAL_VOUCHER) ->unique(Shipment::class, 'tracking_reference', ignorable: $record), ]) ->action(fn (Shipment $record, array $data) => $record->update([ 'tracking_reference' => filled($data['tracking_reference']) ? trim($data['tracking_reference']) : null, ])) - ->visible(fn (Shipment $record) => $record->source === Shipment::SOURCE_MANUAL && ! $record->isCancelled()), + ->visible(fn (Shipment $record) => in_array($record->source, [Shipment::SOURCE_MANUAL, Shipment::SOURCE_MANUAL_VOUCHER], true) && ! $record->isCancelled()), Action::make('add_tracking_update') ->label('Add tracking update') ->icon('heroicon-o-plus-circle') @@ -152,7 +153,7 @@ class OrderShipmentsExtension extends ViewPageExtension ->helperText('Shown to the customer on their order page.'), ]) ->action(fn (Shipment $record, array $data) => $this->addTrackingUpdate($record, $data)) - ->visible(fn (Shipment $record) => $record->source === Shipment::SOURCE_MANUAL && ! $record->isCancelled()), + ->visible(fn (Shipment $record) => in_array($record->source, [Shipment::SOURCE_MANUAL, Shipment::SOURCE_MANUAL_VOUCHER], true) && ! $record->isCancelled()), Action::make('cancel_shipment') ->label('Cancel') ->icon('heroicon-o-x-circle') diff --git a/src/Shipping/Extensions/OrderViewExtension.php b/src/Shipping/Extensions/OrderViewExtension.php index c95cf84..573f9cd 100644 --- a/src/Shipping/Extensions/OrderViewExtension.php +++ b/src/Shipping/Extensions/OrderViewExtension.php @@ -130,7 +130,7 @@ class OrderViewExtension extends ViewPageExtension return [ TextInput::make('tracking_reference') ->label('Voucher / tracking number') - ->unique(Shipment::class, 'tracking_reference') + ->unique(Shipment::class, 'tracking_reference', ignoreRecord: false) ->helperText('Optional — add it later from the shipment if the courier hasn\'t given one yet.'), TextInput::make('package_count') ->label('Number of packages') @@ -305,7 +305,7 @@ class OrderViewExtension extends ViewPageExtension TextInput::make('voucher_number') ->label('Voucher number') ->required() - ->unique(Shipment::class, 'tracking_reference') + ->unique(Shipment::class, 'tracking_reference', ignoreRecord: false) ->live(onBlur: true) ->afterStateUpdated(fn (Set $set) => $set('lookup', null)) ->suffixAction( diff --git a/src/Shipping/Extensions/ShippingMethodResourceExtension.php b/src/Shipping/Extensions/ShippingMethodResourceExtension.php index 7e6acc6..ca6a6b9 100644 --- a/src/Shipping/Extensions/ShippingMethodResourceExtension.php +++ b/src/Shipping/Extensions/ShippingMethodResourceExtension.php @@ -145,9 +145,8 @@ class ShippingMethodResourceExtension extends ResourceExtension ->required($isManual), TextInput::make('tracking_url') ->label('Tracking URL') - ->helperText('Optional. The carrier\'s tracking page, with {number} where the voucher number goes, e.g. https://example.com/track?number={number}.') - ->rule('starts_with:http://,https://') - ->visible($isManual), + ->helperText('Optional. The carrier\'s tracking page, with {number} where the voucher number goes, e.g. https://example.com/track?number={number}. For an integrated carrier it\'s only used on vouchers typed in by hand.') + ->rule('starts_with:http://,https://'), Toggle::make('collects_cash') ->label('Courier collects cash on delivery') ->helperText('Offers cash on delivery at checkout for this shipping method.') diff --git a/src/Shipping/Models/Shipment.php b/src/Shipping/Models/Shipment.php index ba63f03..f8988b1 100644 --- a/src/Shipping/Models/Shipment.php +++ b/src/Shipping/Models/Shipment.php @@ -67,15 +67,15 @@ class Shipment extends Model } /** - * 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. + * A link to the carrier's own tracking page — manual carriers and + * typed-in vouchers only. API-created shipments' 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)) { + if (! in_array($this->source, [self::SOURCE_MANUAL, self::SOURCE_MANUAL_VOUCHER], true) || blank($template) || blank($this->tracking_reference)) { return null; }