createShipmentAction(); 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->shipments()->exists() === false && $this->resolveFulfillmentService($record) !== null); } 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]); } }