194 lines
10 KiB
PHP
194 lines
10 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Services\Parcel2Go;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Http\Response;
|
|
use Illuminate\View\View;
|
|
use Throwable;
|
|
|
|
class ShippingController extends Controller
|
|
{
|
|
public function index(Request $request, Parcel2Go $parcel2Go): View
|
|
{
|
|
abort_unless($request->user()->role === 'admin', 403);
|
|
try {
|
|
$account = [];
|
|
$balance = [];
|
|
$orders = [];
|
|
$profileAvailable = $parcel2Go->hasScope('my-profile');
|
|
$profileNotice = null;
|
|
|
|
if ($profileAvailable) {
|
|
try {
|
|
$account = $parcel2Go->fetch('me/detail');
|
|
$balance = $parcel2Go->fetch('me/prepay/balance');
|
|
$orders = $parcel2Go->fetch('me/orders/detail');
|
|
} catch (Throwable $exception) {
|
|
report($exception);
|
|
$profileNotice = 'Parcel2Go connected, but account history is unavailable. Check that this client has my-profile access.';
|
|
}
|
|
} else {
|
|
$parcel2Go->verifyCredentials();
|
|
$profileNotice = 'Parcel2Go connected for quotes and collections. Add my-profile access in Parcel2Go to show account history and labels.';
|
|
}
|
|
|
|
return view('shipping', compact('account', 'balance', 'orders', 'profileAvailable', 'profileNotice') + [
|
|
'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' => [],
|
|
'profileAvailable' => false,
|
|
'profileNotice' => null,
|
|
'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',
|
|
]);
|
|
}
|
|
}
|