user()->role === 'admin', 403); try { $account = $parcel2Go->fetch('me/detail'); $balance = $parcel2Go->fetch('me/prepay/balance'); $orders = $parcel2Go->fetch('me/orders/detail'); return view('shipping', compact('account', 'balance', 'orders') + [ 'quotes' => $request->session()->get('shipping_quotes', []), 'collectionDates' => $request->session()->get('shipping_collection_dates', []), 'selectedService' => $request->session()->get('shipping_selected_service'), 'bookingResult' => $request->session()->get('shipping_booking_result'), 'bookingPending' => $request->session()->get('shipping_booking_pending', false), ]); } catch (Throwable $exception) { report($exception); return view('shipping', [ 'account' => [], 'balance' => [], 'orders' => [], 'quotes' => $request->session()->get('shipping_quotes', []), 'collectionDates' => $request->session()->get('shipping_collection_dates', []), 'selectedService' => $request->session()->get('shipping_selected_service'), 'bookingResult' => $request->session()->get('shipping_booking_result'), 'bookingPending' => $request->session()->get('shipping_booking_pending', false), 'integrationError' => 'Parcel2Go is unavailable. Check API credentials and connectivity.', ]); } } public function quote(Request $request, Parcel2Go $parcel2Go): RedirectResponse { abort_unless($request->user()->role === 'admin', 403); if ($request->session()->get('shipping_booking_pending')) { return redirect()->route('shipping')->withErrors(['collection' => 'A booking outcome is uncertain. Check your Parcel2Go account before trying again.']); } $details = $request->validate([ 'collection_postcode' => ['required', 'string', 'max:12'], 'delivery_postcode' => ['required', 'string', 'max:12'], 'weight' => ['required', 'numeric', 'gt:0', 'lte:30'], 'length' => ['required', 'numeric', 'gt:0', 'lte:200'], 'width' => ['required', 'numeric', 'gt:0', 'lte:200'], 'height' => ['required', 'numeric', 'gt:0', 'lte:200'], 'value' => ['required', 'numeric', 'gte:0', 'lte:10000'], ]); try { $quotes = $parcel2Go->quote($details)['Quotes'] ?? []; $request->session()->put('shipping_quote', $details); $request->session()->put('shipping_quotes', $quotes); $request->session()->forget(['shipping_collection_dates', 'shipping_selected_service']); return redirect()->route('shipping')->with('status', count($quotes).' services available.'); } catch (Throwable $exception) { report($exception); return redirect()->route('shipping')->withErrors(['quote' => 'Parcel2Go could not quote this parcel. Check the postcodes and parcel details.']); } } public function collectionDates(Request $request, Parcel2Go $parcel2Go): RedirectResponse { abort_unless($request->user()->role === 'admin', 403); $data = $request->validate([ 'service' => ['required', 'string', 'max:200'], 'sender_street' => ['required', 'string', 'max:200'], 'sender_town' => ['required', 'string', 'max:100'], ]); $details = $request->session()->get('shipping_quote'); $quote = collect($request->session()->get('shipping_quotes', []))->first(fn ($quote) => ($quote['Service']['Slug'] ?? null) === $data['service'] && ($quote['Service']['CollectionType'] ?? null) === 'Collection'); if (! $details || ! $quote) { return redirect()->route('shipping')->withErrors(['service' => 'Select a collection service from a current quote.']); } try { $dates = $parcel2Go->collectionDates($data['service'], $data['sender_street'], $data['sender_town'], $details['collection_postcode'])['CollectionDates'] ?? []; $request->session()->put('shipping_collection_dates', $dates); $request->session()->put('shipping_selected_service', $data['service']); $request->session()->put('shipping_sender_location', $data); return redirect()->route('shipping')->with('status', 'Choose a collection date and enter the addresses.'); } catch (Throwable $exception) { report($exception); return redirect()->route('shipping')->withErrors(['collection' => 'Parcel2Go could not return collection dates for this service.']); } } public function bookCollection(Request $request, Parcel2Go $parcel2Go): RedirectResponse { abort_unless($request->user()->role === 'admin', 403); if ($request->session()->get('shipping_booking_pending')) { return redirect()->route('shipping')->withErrors(['collection' => 'A booking outcome is uncertain. Check your Parcel2Go account before trying again.']); } $booking = $request->validate([ 'collection_date' => ['required', 'date', 'after:now'], 'sender_name' => ['required', 'string', 'max:100'], 'sender_email' => ['required', 'email', 'max:150'], 'sender_phone' => ['required', 'string', 'max:30'], 'sender_property' => ['required', 'string', 'max:100'], 'sender_street' => ['required', 'string', 'max:200'], 'sender_town' => ['required', 'string', 'max:100'], 'recipient_name' => ['required', 'string', 'max:100'], 'recipient_email' => ['required', 'email', 'max:150'], 'recipient_phone' => ['required', 'string', 'max:30'], 'recipient_property' => ['required', 'string', 'max:100'], 'recipient_street' => ['required', 'string', 'max:200'], 'recipient_town' => ['required', 'string', 'max:100'], 'contents' => ['required', 'string', 'max:200'], ]); $details = $request->session()->get('shipping_quote'); $service = $request->session()->get('shipping_selected_service'); $dates = $request->session()->get('shipping_collection_dates', []); $senderLocation = $request->session()->get('shipping_sender_location', []); $selectedQuote = collect($request->session()->get('shipping_quotes', [])) ->first(fn ($quote) => ($quote['Service']['Slug'] ?? null) === $service && ($quote['Service']['CollectionType'] ?? null) === 'Collection'); if (! $details || ! $service || ! collect($dates)->contains(fn ($date) => ($date['CollectionDate'] ?? null) === $booking['collection_date']) || ! $selectedQuote || ! empty($selectedQuote['RequiresCustoms']) || ! empty($selectedQuote['RequiresCommercialInvoice']) || ($senderLocation['sender_street'] ?? null) !== $booking['sender_street'] || ($senderLocation['sender_town'] ?? null) !== $booking['sender_town']) { return redirect()->route('shipping')->withErrors(['collection' => 'Choose an available collection date for the quoted address.']); } try { $request->session()->put('shipping_booking_pending', true); $result = $parcel2Go->bookCollection($details, $service, $booking); if (empty($result['OrderId'])) { throw new \RuntimeException('Parcel2Go did not return an order reference.'); } $request->session()->forget(['shipping_quote', 'shipping_quotes', 'shipping_collection_dates', 'shipping_selected_service', 'shipping_sender_location', 'shipping_booking_pending']); return redirect()->route('shipping')->with('shipping_booking_result', $result)->with('status', 'Collection order created. Complete payment with Parcel2Go.'); } catch (Throwable $exception) { report($exception); return redirect()->route('shipping')->withErrors(['collection' => 'Booking outcome is uncertain. Check your Parcel2Go account before trying again.']); } } public function resetBooking(Request $request): RedirectResponse { abort_unless($request->user()->role === 'admin', 403); $request->validate(['checked_account' => ['accepted']]); $request->session()->forget(['shipping_quote', 'shipping_quotes', 'shipping_collection_dates', 'shipping_selected_service', 'shipping_sender_location', 'shipping_booking_pending']); return redirect()->route('shipping')->with('status', 'Booking workflow reset. Start with a fresh quote.'); } public function label(Request $request, Parcel2Go $parcel2Go, string $orderLineId): Response { abort_unless($request->user()->role === 'admin', 403); abort_unless(ctype_digit($orderLineId), 404); $orders = $parcel2Go->fetch('me/orders/detail')['Orders'] ?? []; $order = collect($orders)->first(fn ($order) => (string) ($order['OrderLineId'] ?? '') === $orderLineId); abort_unless($order && ! empty($order['Hash']), 404); $pdf = $parcel2Go->labelPdf($orderLineId, $order['Hash']); return response($pdf, 200, [ 'Content-Type' => 'application/pdf', 'Content-Disposition' => 'attachment; filename="parcel2go-'.$orderLineId.'.pdf"', 'Cache-Control' => 'private, no-store', ]); } }