This commit is contained in:
@@ -7,6 +7,7 @@ use Modules\Booking\Data\CreateBookingData;
|
||||
use Modules\Booking\Data\VehicleSelectionData;
|
||||
use Modules\Booking\Enums\BookingStatus;
|
||||
use Modules\Booking\Events\BookingCreated;
|
||||
use Modules\Booking\Exceptions\InvalidReturnRouteException;
|
||||
use Modules\Booking\Models\Booking;
|
||||
use Modules\Booking\Services\BookingRefGenerator;
|
||||
use Modules\Booking\Services\BookingService;
|
||||
@@ -26,50 +27,110 @@ class CreateBookingAction
|
||||
{
|
||||
$this->bookingService->validateSelections($data->selections);
|
||||
|
||||
return DB::transaction(function () use ($data) {
|
||||
$route = EvRoute::findOrFail($data->evRouteId);
|
||||
$isRoundTrip = $data->returnEvRouteId !== null;
|
||||
|
||||
$lines = array_map(
|
||||
fn (VehicleSelectionData $selection) => $this->priceSelection($route, $selection),
|
||||
$data->selections,
|
||||
if ($isRoundTrip) {
|
||||
$this->bookingService->validateSelections($data->returnSelections);
|
||||
}
|
||||
|
||||
return DB::transaction(function () use ($data, $isRoundTrip) {
|
||||
$outboundRoute = EvRoute::findOrFail($data->evRouteId);
|
||||
|
||||
$outboundBooking = $this->createLeg(
|
||||
data: $data,
|
||||
route: $outboundRoute,
|
||||
selections: $data->selections,
|
||||
travelDate: $data->travelDate,
|
||||
timeSlotId: $data->departureTimeSlotId,
|
||||
isReturnLeg: false,
|
||||
);
|
||||
|
||||
$totalPrice = array_reduce(
|
||||
$lines,
|
||||
fn (string $carry, array $line) => bcadd($carry, $line['line_total'], 2),
|
||||
'0.00',
|
||||
if (! $isRoundTrip) {
|
||||
BookingCreated::dispatch($outboundBooking);
|
||||
|
||||
return $outboundBooking;
|
||||
}
|
||||
|
||||
$returnRoute = EvRoute::findOrFail($data->returnEvRouteId);
|
||||
|
||||
if (! $returnRoute->isReverseOf($outboundRoute)) {
|
||||
throw InvalidReturnRouteException::notReverseOfOutbound($returnRoute, $outboundRoute);
|
||||
}
|
||||
|
||||
$returnBooking = $this->createLeg(
|
||||
data: $data,
|
||||
route: $returnRoute,
|
||||
selections: $data->returnSelections,
|
||||
travelDate: $data->returnTravelDate,
|
||||
timeSlotId: $data->returnDepartureTimeSlotId,
|
||||
isReturnLeg: true,
|
||||
);
|
||||
|
||||
$booking = Booking::create([
|
||||
'booking_ref' => $this->bookingRefGenerator->generate(),
|
||||
'user_id' => $data->userId,
|
||||
'openid' => $data->openid,
|
||||
'ev_route_id' => $data->evRouteId,
|
||||
'departure_time_slot_id' => $data->departureTimeSlotId,
|
||||
'travel_date' => $data->travelDate,
|
||||
'passenger_name' => $data->passengerName,
|
||||
'passenger_phone' => $data->passengerPhone,
|
||||
'pickup_address' => $data->pickupAddress,
|
||||
'pickup_lat' => $data->pickupLat,
|
||||
'pickup_lng' => $data->pickupLng,
|
||||
'dropoff_address' => $data->dropoffAddress,
|
||||
'dropoff_lat' => $data->dropoffLat,
|
||||
'dropoff_lng' => $data->dropoffLng,
|
||||
'price' => $totalPrice,
|
||||
'status' => BookingStatus::PendingPayment,
|
||||
'is_round_trip' => $data->isRoundTrip,
|
||||
'return_travel_date' => $data->returnTravelDate,
|
||||
'created_by_channel' => $data->createdByChannel,
|
||||
]);
|
||||
// Linked bidirectionally after both rows exist — a single
|
||||
// `linked_booking_id` FK can't be set on either row at create
|
||||
// time since the other side doesn't have an id yet.
|
||||
$returnBooking->update(['linked_booking_id' => $outboundBooking->id]);
|
||||
$outboundBooking->update(['linked_booking_id' => $returnBooking->id]);
|
||||
|
||||
$booking->vehicleOptions()->createMany($lines);
|
||||
// No registered listeners on BookingCreated today, so firing it
|
||||
// twice per round-trip creation has no side effects — flagged
|
||||
// here for whoever adds the first listener.
|
||||
BookingCreated::dispatch($outboundBooking);
|
||||
BookingCreated::dispatch($returnBooking);
|
||||
|
||||
BookingCreated::dispatch($booking);
|
||||
|
||||
return $booking;
|
||||
return $outboundBooking->refresh();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<VehicleSelectionData> $selections
|
||||
*/
|
||||
private function createLeg(
|
||||
CreateBookingData $data,
|
||||
EvRoute $route,
|
||||
array $selections,
|
||||
string $travelDate,
|
||||
int $timeSlotId,
|
||||
bool $isReturnLeg,
|
||||
): Booking {
|
||||
$lines = array_map(
|
||||
fn (VehicleSelectionData $selection) => $this->priceSelection($route, $selection),
|
||||
$selections,
|
||||
);
|
||||
|
||||
$totalPrice = array_reduce(
|
||||
$lines,
|
||||
fn (string $carry, array $line) => bcadd($carry, $line['line_total'], 2),
|
||||
'0.00',
|
||||
);
|
||||
|
||||
$booking = Booking::create([
|
||||
'booking_ref' => $this->bookingRefGenerator->generate(),
|
||||
'user_id' => $data->userId,
|
||||
'openid' => $data->openid,
|
||||
'ev_route_id' => $route->id,
|
||||
'is_return_leg' => $isReturnLeg,
|
||||
'departure_time_slot_id' => $timeSlotId,
|
||||
'travel_date' => $travelDate,
|
||||
'passenger_name' => $data->passengerName,
|
||||
'passenger_phone' => $data->passengerPhone,
|
||||
'notes' => $data->notes,
|
||||
'pickup_address' => $data->pickupAddress,
|
||||
'pickup_lat' => $data->pickupLat,
|
||||
'pickup_lng' => $data->pickupLng,
|
||||
'dropoff_address' => $data->dropoffAddress,
|
||||
'dropoff_lat' => $data->dropoffLat,
|
||||
'dropoff_lng' => $data->dropoffLng,
|
||||
'price' => $totalPrice,
|
||||
'status' => BookingStatus::PendingPayment,
|
||||
'created_by_channel' => $data->createdByChannel,
|
||||
]);
|
||||
|
||||
$booking->vehicleOptions()->createMany($lines);
|
||||
|
||||
return $booking;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{vehicle_option: VehicleOption, passenger_count: int, unit_price: string, line_total: string}
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user