Add Booking module: model, create/read/cancel API, Filament resource (T4.1-T4.7)
- Booking model with booking_vehicle_options line items (supports mixing vehicle options like front_seat + back_seat in one booking), price snapshot, status machine, and driver/car assignment fields - BookingService: front-seat max, disabled-option toggles, duplicate-option and whole-vehicle-exclusivity guards - CreateBookingAction, CancelBookingAction, AssignDriverAction - BookingRefGenerator: sequential EVB-AAAAA1-style refs via row lock - POST/GET/cancel booking API endpoints (Sanctum, ownership + admin policy) - BookingPlugin + Filament BookingResource: list, detail view, Cancel and Assign Driver actions (shared between table and detail page) - domain.md updated for multi-vehicle-option bookings (§2) and driver/ vehicle assignment (§5a)
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Booking\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Modules\Booking\Actions\CancelBookingAction;
|
||||
use Modules\Booking\Actions\CreateBookingAction;
|
||||
use Modules\Booking\Data\CreateBookingData;
|
||||
use Modules\Booking\Data\VehicleSelectionData;
|
||||
use Modules\Booking\Enums\BookingChannel;
|
||||
use Modules\Booking\Http\Requests\StoreBookingRequest;
|
||||
use Modules\Booking\Http\Resources\BookingResource;
|
||||
use Modules\Booking\Models\Booking;
|
||||
use Modules\Shared\Enums\VehicleOption;
|
||||
|
||||
class BookingController extends Controller
|
||||
{
|
||||
/**
|
||||
* @var list<string>
|
||||
*/
|
||||
private const EAGER_LOADS = ['route', 'timeSlot', 'vehicleOptions'];
|
||||
|
||||
public function __construct(
|
||||
private CreateBookingAction $createBookingAction,
|
||||
private CancelBookingAction $cancelBookingAction,
|
||||
) {}
|
||||
|
||||
public function index(Request $request): AnonymousResourceCollection
|
||||
{
|
||||
Gate::authorize('viewAny', Booking::class);
|
||||
|
||||
$bookings = Booking::query()
|
||||
->where('user_id', $request->user()->id)
|
||||
->with(self::EAGER_LOADS)
|
||||
->latest()
|
||||
->paginate();
|
||||
|
||||
return BookingResource::collection($bookings);
|
||||
}
|
||||
|
||||
public function show(Booking $booking): BookingResource
|
||||
{
|
||||
Gate::authorize('view', $booking);
|
||||
|
||||
return new BookingResource($booking->load(self::EAGER_LOADS));
|
||||
}
|
||||
|
||||
public function store(StoreBookingRequest $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
|
||||
$selections = array_map(
|
||||
fn (array $selection) => new VehicleSelectionData(
|
||||
vehicleOption: VehicleOption::from($selection['vehicle_option']),
|
||||
passengerCount: $selection['passenger_count'],
|
||||
),
|
||||
$validated['selections'],
|
||||
);
|
||||
|
||||
$booking = $this->createBookingAction->handle(new CreateBookingData(
|
||||
evRouteId: $validated['ev_route_id'],
|
||||
departureTimeSlotId: $validated['departure_time_slot_id'],
|
||||
travelDate: $validated['travel_date'],
|
||||
selections: $selections,
|
||||
passengerName: $validated['passenger_name'],
|
||||
passengerPhone: $validated['passenger_phone'],
|
||||
pickupAddress: $validated['pickup_address'],
|
||||
dropoffAddress: $validated['dropoff_address'],
|
||||
createdByChannel: isset($validated['created_by_channel'])
|
||||
? BookingChannel::from($validated['created_by_channel'])
|
||||
: BookingChannel::MiniApp,
|
||||
userId: $request->user()?->id,
|
||||
openid: $validated['openid'] ?? null,
|
||||
pickupLat: $validated['pickup_lat'] ?? null,
|
||||
pickupLng: $validated['pickup_lng'] ?? null,
|
||||
dropoffLat: $validated['dropoff_lat'] ?? null,
|
||||
dropoffLng: $validated['dropoff_lng'] ?? null,
|
||||
isRoundTrip: $validated['is_round_trip'] ?? false,
|
||||
returnTravelDate: $validated['return_travel_date'] ?? null,
|
||||
));
|
||||
|
||||
return (new BookingResource($booking->load(self::EAGER_LOADS)))
|
||||
->response()
|
||||
->setStatusCode(201);
|
||||
}
|
||||
|
||||
public function cancel(Booking $booking): BookingResource
|
||||
{
|
||||
Gate::authorize('cancel', $booking);
|
||||
|
||||
$this->cancelBookingAction->handle($booking);
|
||||
|
||||
return new BookingResource($booking->load(self::EAGER_LOADS));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user