mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-09 03:38:17 +00:00
Added group booking option
This commit is contained in:
@@ -37,6 +37,7 @@ import {
|
||||
import { JwtGuard } from "../../common/jwt.guard";
|
||||
import { PassengerAdmin, PassengerStaff, PassengerStaffStrict } from "../../common/passenger-guards";
|
||||
import { PASSENGER_PERMS } from "../../seed/passenger-permissions.registry";
|
||||
import { SeatsService } from "../seats/seats.service";
|
||||
|
||||
@ApiTags("Booking")
|
||||
@Controller("bookings")
|
||||
@@ -45,6 +46,7 @@ export class BookingsController {
|
||||
constructor(
|
||||
private service: BookingsService,
|
||||
private guestService: GuestBookingService,
|
||||
private seatsService: SeatsService,
|
||||
) {}
|
||||
|
||||
@Get("my")
|
||||
@@ -359,6 +361,37 @@ export class BookingsController {
|
||||
return this.guestService.createGuestBooking(dto, req);
|
||||
}
|
||||
|
||||
@Post("group")
|
||||
@PassengerStaff([PASSENGER_PERMS.bookings.manage])
|
||||
@ApiBearerAuth("IAM-auth")
|
||||
@ApiOperation({
|
||||
summary: "Create a group booking — staff bulk/group reservation, one PNR for the whole group",
|
||||
description: `Staff-only entry point for bulk/group bookings (e.g. tour groups booked via an uploaded passenger list and auto-assigned seats from POST /seats/auto-assign-hold).
|
||||
|
||||
Same body shape as POST /bookings/guest (CreateGuestBookingDto) and the same underlying pipeline — fare engine, ADULT/CHILD age pricing — just gated to staff and always ONE_WAY.
|
||||
|
||||
Skips Verifayda national-ID verification: the roster comes from a staff-uploaded spreadsheet, not a live Fayda identity flow, so there is nothing to verify an ID number against. Passenger fields (name, DOB, nationality) are trusted exactly as uploaded.
|
||||
|
||||
Deliberately does NOT forward the staff caller's identity into booking creation: the acting staff member is not a Passenger, so the underlying guest-booking flow (which tries to resolve an authenticated caller as an existing Passenger profile) would reject the request. The booking is created exactly like a guest booking — a fresh passenger record, contact info from the first passenger in the list — with staff authorization enforced only at this route.
|
||||
|
||||
If booking creation fails after the seats were already held, the hold is released immediately so the seats don't sit locked for the rest of the hold TTL.`,
|
||||
})
|
||||
@ApiResponse({ status: 201, description: "Group booking created successfully with fareBreakdown" })
|
||||
@ApiResponse({ status: 400, description: "Missing required seat IDs" })
|
||||
async createGroup(@Body() dto: CreateGuestBookingDto) {
|
||||
try {
|
||||
return await this.guestService.createGuestBooking({ ...dto, bookingType: "ONE_WAY", skipIdentityVerification: true });
|
||||
} catch (err) {
|
||||
try {
|
||||
await this.seatsService.releaseHold(dto.holdId);
|
||||
} catch (releaseErr) {
|
||||
// Best-effort — the hold may already be gone (e.g. it expired mid-request). The
|
||||
// original booking-creation error is what the caller actually needs to see.
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
@Post("reservations/:seatId/issue")
|
||||
@PassengerStaffStrict(PASSENGER_PERMS.tickets.generate)
|
||||
@ApiBearerAuth("IAM-auth")
|
||||
|
||||
@@ -168,6 +168,15 @@ export class CreateGuestBookingDto {
|
||||
|
||||
@ApiPropertyOptional({ description: 'Total amount in minor units (ETB) as computed and displayed on the review page. When provided, overrides the fare engine total — use to pass the exact berth-specific amount the user saw.' })
|
||||
@IsOptional() @IsNumber() reviewedTotalMinor?: number;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description:
|
||||
'Skip Verifayda national-ID verification and trust passenger fields as given (name, DOB, nationality). ' +
|
||||
'For staff-entered/bulk-uploaded rosters (e.g. group bookings) where there is no live Fayda identity ' +
|
||||
'flow to verify against — calling Verifayda for typed-in ID numbers either returns dev-mode mock data ' +
|
||||
'(overwriting the real name) or, once configured, would reject the whole booking on a non-match.',
|
||||
})
|
||||
@IsOptional() @IsBoolean() skipIdentityVerification?: boolean;
|
||||
}
|
||||
|
||||
export class SavedPassengerProfileDto {
|
||||
|
||||
@@ -196,7 +196,7 @@ export class GuestBookingService {
|
||||
passenger.idDocumentType === IdDocumentType.NATIONAL_ID;
|
||||
|
||||
if (isEthiopian && passenger.idDocumentType === IdDocumentType.NATIONAL_ID) {
|
||||
if (passenger.idDocumentNumber) {
|
||||
if (passenger.idDocumentNumber && !dto.skipIdentityVerification) {
|
||||
const verification = await this.verifaydaService.verifyNationalId(passenger.idDocumentNumber);
|
||||
if (!verification.verified) {
|
||||
throw new BadRequestException(
|
||||
|
||||
Reference in New Issue
Block a user