createShipmentAction(); $actions[] = $this->markPickedUpAction(); return $actions; } private function createShipmentAction(): Action { return Action::make('create_shipment') ->label('Create Shipment') ->icon('heroicon-o-truck') ->modalSubmitActionLabel('Create Shipment') ->schema([ TextInput::make('weight') ->label('Package weight (kg)') ->numeric() ->minValue(0) ->helperText('Leave blank to use the carrier\'s default.'), TextInput::make('destination_location_id') ->label('Box Now locker ID') ->helperText('Only required for Box Now shipments.') ->default(fn (Order $record) => $record->shippingAddress?->meta['box_now_locker']['locationId'] ?? null), Toggle::make('confirm') ->label('Confirm') ->helperText('This will create a real shipment with the carrier.') ->rules([ function () { return function (string $attribute, $value, Closure $fail) { if ($value !== true) { $fail('Please confirm before creating the shipment.'); } }; }, ]), ]) ->action(function (Order $record, array $data, Action $action) { $service = $this->resolveFulfillmentService($record); if (! $service) { Notification::make() ->title('No carrier fulfillment integration is configured for this order.') ->danger() ->send(); $action->halt(); return; } $request = new ShipmentRequest( weight: filled($data['weight'] ?? null) ? (float) $data['weight'] : null, destinationLocationId: $data['destination_location_id'] ?? null, ); try { $service->createShipment($record, $request); } catch (Throwable $e) { report($e); Notification::make() ->title('Failed to create shipment: '.$e->getMessage()) ->danger() ->send(); $action->halt(); return; } Notification::make() ->title('Shipment created.') ->success() ->send(); }) ->visible(fn (Order $record) => $record->status === 'ready-for-dispatch' && ! $record->isStorePickupOrder() && $record->shipments()->exists() === false && $this->resolveFulfillmentService($record) !== null); } /** * The store-pickup mirror of createShipmentAction() — a store-pickup * order never gets a Shipment record (no carrier is ever involved), so * it needs its own way to close out of 'ready-for-pickup' once the * customer has actually collected it. Sets status directly to * 'completed', same terminal status DeriveOrderDeliveredFromShipment * writes for a carrier order once tracking confirms delivery — see * that listener's own docblock. */ private function markPickedUpAction(): Action { return Action::make('mark_picked_up') ->label('Mark Picked Up') ->icon('heroicon-o-check-circle') ->color('success') ->requiresConfirmation() ->modalDescription('Confirms the customer has collected this order in store.') ->action(function (Order $record) { $record->update(['status' => 'completed']); Notification::make() ->title('Order marked as picked up.') ->success() ->send(); }) ->visible(fn (Order $record) => $record->status === 'ready-for-pickup' && $record->isStorePickupOrder()); } private function resolveCarrier(Order $record): ?string { $code = $record->shippingAddress?->shipping_option; if (! $code) { return null; } return ShippingMethod::where('code', $code)->value('driver'); } private function resolveFulfillmentService(Order $record): ?CarrierFulfillmentInterface { $carrier = $this->resolveCarrier($record); if (! $carrier) { return null; } return app(CarrierFulfillmentInterface::class, ['carrier' => $carrier]); } }