mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 12:18:11 +00:00
Seat hold updates
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { Body, Controller, Delete, Get, Param, Post, Patch, UseGuards, Query, Req, BadRequestException } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiBearerAuth, ApiResponse, ApiQuery } from '@nestjs/swagger';
|
||||
import { ApiTags, ApiOperation, ApiBearerAuth, ApiResponse, ApiQuery, ApiBody } from '@nestjs/swagger';
|
||||
import { BookingsService } from './bookings.service';
|
||||
import { GuestBookingService } from './guest-booking.service';
|
||||
import { CreateBookingDto, ModifyBookingDto, CancelBookingDto } from './bookings.dto';
|
||||
@@ -100,35 +100,153 @@ export class BookingsController {
|
||||
|
||||
@Post('guest')
|
||||
@ApiOperation({
|
||||
summary: 'Create guest booking without login (optional account creation)',
|
||||
description: `Creates a booking without requiring login. Features:
|
||||
|
||||
**Guest Checkout:**
|
||||
- No login required
|
||||
- Contact details from first passenger
|
||||
- Booking confirmation sent to email/phone
|
||||
summary: 'Create guest booking — ONE_WAY | ROUND_TRIP | TRANSIT | ROUND_TRIP_TRANSIT (no login required)',
|
||||
description: `Creates a booking without requiring login. Supports all four booking types.
|
||||
|
||||
**Optional Account Creation:**
|
||||
- Set createAccount=true with password
|
||||
- Account created using first passenger details
|
||||
- Automatic login after booking
|
||||
- Loyalty points and wallet created
|
||||
**bookingType: ONE_WAY (default)**
|
||||
- scheduleId, holdId, originStationId, destinationStationId, seatClassId
|
||||
- passengers[]: { seatId, passengerName, dateOfBirth, idDocumentType, … }
|
||||
|
||||
**Passenger Details Storage:**
|
||||
- savePassengerDetails=true: Save for future bookings
|
||||
- Stored by userId (if account created) or deviceId
|
||||
- Retrieve saved passengers for quick booking
|
||||
**bookingType: ROUND_TRIP**
|
||||
- Above + returnScheduleId, returnHoldId, returnOriginStationId, returnDestinationStationId, returnSeatClassId
|
||||
- passengers[]: each must include returnSeatId (seat on the return leg)
|
||||
|
||||
**Verifayda Verification:**
|
||||
- Ethiopian nationals: National ID verified via Verifayda
|
||||
- Other nationals: Passport details (no verification)
|
||||
**bookingType: TRANSIT**
|
||||
- scheduleId/holdId (leg-1) + leg2ScheduleId, leg2HoldId, transitStationId, leg2DestinationStationId
|
||||
- passengers[]: each must include leg2SeatId
|
||||
|
||||
**Age-Based Pricing:**
|
||||
- ADULT (≥5 years): Full fare
|
||||
- CHILD (<5 years): First child FREE, subsequent children full fare`
|
||||
**bookingType: ROUND_TRIP_TRANSIT**
|
||||
- All TRANSIT outbound fields + returnScheduleId/returnHoldId/returnOriginStationId/returnDestinationStationId/returnLeg2ScheduleId/returnLeg2HoldId/returnTransitStationId/returnLeg2DestinationStationId
|
||||
- passengers[]: each must include leg2SeatId, returnSeatId, returnLeg2SeatId
|
||||
|
||||
**Optional account creation:** set createAccount=true with password — creates account from first passenger details, loyalty + wallet initialised.
|
||||
|
||||
**Verifayda:** Ethiopian nationals verified; international passengers require passportNumber + passportCountry.`
|
||||
})
|
||||
@ApiResponse({ status: 201, description: 'Booking created successfully' })
|
||||
@ApiResponse({ status: 400, description: 'Verifayda verification failed or invalid data' })
|
||||
@ApiBody({
|
||||
type: CreateGuestBookingDto,
|
||||
examples: {
|
||||
ONE_WAY: {
|
||||
summary: 'ONE_WAY — single direct journey (guest)',
|
||||
value: {
|
||||
scheduleId: 'schedule-uuid',
|
||||
holdId: 'hold-uuid',
|
||||
originStationId: 'station-uuid',
|
||||
destinationStationId: 'station-uuid',
|
||||
seatClassId: 'seat-class-uuid',
|
||||
bookingType: 'ONE_WAY',
|
||||
displayCurrency: 'ETB',
|
||||
passengers: [{
|
||||
seatId: 'seat-uuid',
|
||||
passengerName: 'Abebe Kebede',
|
||||
dateOfBirth: '1990-05-15',
|
||||
idDocumentType: 'NATIONAL_ID',
|
||||
idDocumentNumber: 'ET123456789',
|
||||
nationality: 'Ethiopian',
|
||||
phone: '+251911234567',
|
||||
email: 'abebe@email.com',
|
||||
}],
|
||||
savePassengerDetails: true,
|
||||
deviceId: 'device-uuid-123',
|
||||
},
|
||||
},
|
||||
ROUND_TRIP: {
|
||||
summary: 'ROUND_TRIP — outbound + return, single PNR (guest)',
|
||||
value: {
|
||||
scheduleId: 'outbound-schedule-uuid',
|
||||
holdId: 'outbound-hold-uuid',
|
||||
originStationId: 'addis-station-uuid',
|
||||
destinationStationId: 'djibouti-station-uuid',
|
||||
seatClassId: 'seat-class-uuid',
|
||||
bookingType: 'ROUND_TRIP',
|
||||
returnScheduleId: 'return-schedule-uuid',
|
||||
returnHoldId: 'return-hold-uuid',
|
||||
returnOriginStationId: 'djibouti-station-uuid',
|
||||
returnDestinationStationId: 'addis-station-uuid',
|
||||
returnSeatClassId: 'seat-class-uuid',
|
||||
displayCurrency: 'ETB',
|
||||
passengers: [{
|
||||
seatId: 'outbound-seat-uuid',
|
||||
returnSeatId: 'return-seat-uuid',
|
||||
passengerName: 'Abebe Kebede',
|
||||
dateOfBirth: '1990-05-15',
|
||||
idDocumentType: 'NATIONAL_ID',
|
||||
idDocumentNumber: 'ET123456789',
|
||||
nationality: 'Ethiopian',
|
||||
phone: '+251911234567',
|
||||
}],
|
||||
savePassengerDetails: true,
|
||||
deviceId: 'device-uuid-123',
|
||||
},
|
||||
},
|
||||
TRANSIT: {
|
||||
summary: 'TRANSIT — connecting train, single PNR (guest)',
|
||||
value: {
|
||||
scheduleId: 'leg1-schedule-uuid',
|
||||
holdId: 'leg1-hold-uuid',
|
||||
originStationId: 'addis-station-uuid',
|
||||
destinationStationId: 'diredawa-station-uuid',
|
||||
seatClassId: 'seat-class-uuid',
|
||||
bookingType: 'TRANSIT',
|
||||
leg2ScheduleId: 'leg2-schedule-uuid',
|
||||
leg2HoldId: 'leg2-hold-uuid',
|
||||
transitStationId: 'diredawa-station-uuid',
|
||||
leg2DestinationStationId: 'djibouti-station-uuid',
|
||||
displayCurrency: 'ETB',
|
||||
passengers: [{
|
||||
seatId: 'leg1-seat-uuid',
|
||||
leg2SeatId: 'leg2-seat-uuid',
|
||||
passengerName: 'Abebe Kebede',
|
||||
dateOfBirth: '1990-05-15',
|
||||
idDocumentType: 'NATIONAL_ID',
|
||||
idDocumentNumber: 'ET123456789',
|
||||
nationality: 'Ethiopian',
|
||||
phone: '+251911234567',
|
||||
}],
|
||||
deviceId: 'device-uuid-123',
|
||||
},
|
||||
},
|
||||
ROUND_TRIP_TRANSIT: {
|
||||
summary: 'ROUND_TRIP_TRANSIT — round trip via connecting trains, 4 holds (guest)',
|
||||
value: {
|
||||
scheduleId: 'ob-leg1-schedule-uuid',
|
||||
holdId: 'ob-leg1-hold-uuid',
|
||||
originStationId: 'addis-station-uuid',
|
||||
destinationStationId: 'diredawa-station-uuid',
|
||||
seatClassId: 'seat-class-uuid',
|
||||
bookingType: 'ROUND_TRIP_TRANSIT',
|
||||
leg2ScheduleId: 'ob-leg2-schedule-uuid',
|
||||
leg2HoldId: 'ob-leg2-hold-uuid',
|
||||
transitStationId: 'diredawa-station-uuid',
|
||||
leg2DestinationStationId: 'djibouti-station-uuid',
|
||||
returnScheduleId: 'ret-leg1-schedule-uuid',
|
||||
returnHoldId: 'ret-leg1-hold-uuid',
|
||||
returnOriginStationId: 'djibouti-station-uuid',
|
||||
returnDestinationStationId: 'diredawa-station-uuid',
|
||||
returnLeg2ScheduleId: 'ret-leg2-schedule-uuid',
|
||||
returnLeg2HoldId: 'ret-leg2-hold-uuid',
|
||||
returnTransitStationId: 'diredawa-station-uuid',
|
||||
returnLeg2DestinationStationId: 'addis-station-uuid',
|
||||
displayCurrency: 'ETB',
|
||||
passengers: [{
|
||||
seatId: 'ob-leg1-seat-uuid',
|
||||
leg2SeatId: 'ob-leg2-seat-uuid',
|
||||
returnSeatId: 'ret-leg1-seat-uuid',
|
||||
returnLeg2SeatId: 'ret-leg2-seat-uuid',
|
||||
passengerName: 'Abebe Kebede',
|
||||
dateOfBirth: '1990-05-15',
|
||||
idDocumentType: 'NATIONAL_ID',
|
||||
idDocumentNumber: 'ET123456789',
|
||||
nationality: 'Ethiopian',
|
||||
phone: '+251911234567',
|
||||
}],
|
||||
deviceId: 'device-uuid-123',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@ApiResponse({ status: 201, description: 'Booking created successfully with fareBreakdown' })
|
||||
@ApiResponse({ status: 400, description: 'Missing required seat IDs for bookingType, or Verifayda verification failed' })
|
||||
createGuest(@Body() dto: CreateGuestBookingDto) {
|
||||
return this.guestService.createGuestBooking(dto);
|
||||
}
|
||||
@@ -147,24 +265,149 @@ export class BookingsController {
|
||||
@UseGuards(JwtGuard)
|
||||
@ApiBearerAuth('JWT-auth')
|
||||
@ApiOperation({
|
||||
summary: 'Create booking (one-way or round-trip)',
|
||||
description: `Creates a one-way or round-trip booking for logged-in users.
|
||||
|
||||
ONE_WAY booking:
|
||||
- scheduleId, holdId, originStationId, destinationStationId
|
||||
- passengers: array of PassengerInputDto with seatId
|
||||
- Single PNR, single payment
|
||||
summary: 'Create booking — ONE_WAY | ROUND_TRIP | TRANSIT | ROUND_TRIP_TRANSIT',
|
||||
description: `Creates a booking for a logged-in passenger. bookingType controls which fields are required.
|
||||
|
||||
ROUND_TRIP booking:
|
||||
- Outbound: scheduleId, holdId, originStationId, destinationStationId, seatClassId
|
||||
- Return: returnScheduleId, returnHoldId, returnOriginStationId, returnDestinationStationId, returnSeatClassId
|
||||
- passengers: array of RoundTripPassengerDto with outboundSeatId and returnSeatId
|
||||
- Combined PNR, single payment for both legs
|
||||
- Fare = outbound_fare + return_fare, single total, single promo, single loyalty deduction`
|
||||
**ONE_WAY**
|
||||
- scheduleId, holdId, originStationId, destinationStationId, seatClassId
|
||||
- passengers[]: { seatId, passengerName, dateOfBirth, idDocumentType, … }
|
||||
|
||||
**ROUND_TRIP**
|
||||
- Above + returnScheduleId, returnHoldId, returnOriginStationId, returnDestinationStationId, returnSeatClassId
|
||||
- passengers[]: { seatId (outbound), returnSeatId (return), passengerName, … }
|
||||
- Combined fare = outbound fare + return fare; single promo/loyalty deduction
|
||||
|
||||
**TRANSIT** (connecting train, single PNR)
|
||||
- scheduleId/holdId for leg-1 + leg2ScheduleId, leg2HoldId, transitStationId, leg2DestinationStationId
|
||||
- passengers[]: { seatId (leg-1), leg2SeatId (leg-2), passengerName, … }
|
||||
|
||||
**ROUND_TRIP_TRANSIT** (round trip, each direction via connecting train)
|
||||
- All TRANSIT outbound fields + returnScheduleId, returnHoldId, returnOriginStationId, returnDestinationStationId, returnLeg2ScheduleId, returnLeg2HoldId, returnTransitStationId, returnLeg2DestinationStationId
|
||||
- passengers[]: { seatId, leg2SeatId, returnSeatId, returnLeg2SeatId, passengerName, … }
|
||||
- 4 holds required, 4 seat sets per passenger, single PNR, single payment
|
||||
|
||||
**Age-Based Pricing (all types)**
|
||||
- ADULT (≥5 years): full fare per leg
|
||||
- CHILD (<5 years): first child FREE per booking, subsequent children full fare`
|
||||
})
|
||||
@ApiBody({
|
||||
type: CreateBookingDto,
|
||||
examples: {
|
||||
ONE_WAY: {
|
||||
summary: 'ONE_WAY — single direct journey',
|
||||
value: {
|
||||
passengerId: 'passenger-uuid',
|
||||
scheduleId: 'schedule-uuid',
|
||||
holdId: 'hold-uuid',
|
||||
originStationId: 'station-uuid',
|
||||
destinationStationId: 'station-uuid',
|
||||
seatClassId: 'seat-class-uuid',
|
||||
bookingType: 'ONE_WAY',
|
||||
displayCurrency: 'ETB',
|
||||
passengers: [{
|
||||
seatId: 'seat-uuid',
|
||||
passengerName: 'Abebe Kebede',
|
||||
dateOfBirth: '1990-05-15',
|
||||
idDocumentType: 'NATIONAL_ID',
|
||||
idDocumentNumber: 'ET123456789',
|
||||
nationality: 'Ethiopian',
|
||||
}],
|
||||
},
|
||||
},
|
||||
ROUND_TRIP: {
|
||||
summary: 'ROUND_TRIP — outbound + return, single PNR',
|
||||
value: {
|
||||
passengerId: 'passenger-uuid',
|
||||
scheduleId: 'outbound-schedule-uuid',
|
||||
holdId: 'outbound-hold-uuid',
|
||||
originStationId: 'addis-station-uuid',
|
||||
destinationStationId: 'djibouti-station-uuid',
|
||||
seatClassId: 'seat-class-uuid',
|
||||
bookingType: 'ROUND_TRIP',
|
||||
returnScheduleId: 'return-schedule-uuid',
|
||||
returnHoldId: 'return-hold-uuid',
|
||||
returnOriginStationId: 'djibouti-station-uuid',
|
||||
returnDestinationStationId: 'addis-station-uuid',
|
||||
returnSeatClassId: 'seat-class-uuid',
|
||||
displayCurrency: 'ETB',
|
||||
passengers: [{
|
||||
seatId: 'outbound-seat-uuid',
|
||||
returnSeatId: 'return-seat-uuid',
|
||||
passengerName: 'Abebe Kebede',
|
||||
dateOfBirth: '1990-05-15',
|
||||
idDocumentType: 'NATIONAL_ID',
|
||||
idDocumentNumber: 'ET123456789',
|
||||
nationality: 'Ethiopian',
|
||||
}],
|
||||
},
|
||||
},
|
||||
TRANSIT: {
|
||||
summary: 'TRANSIT — connecting train, single PNR',
|
||||
value: {
|
||||
passengerId: 'passenger-uuid',
|
||||
scheduleId: 'leg1-schedule-uuid',
|
||||
holdId: 'leg1-hold-uuid',
|
||||
originStationId: 'addis-station-uuid',
|
||||
destinationStationId: 'diredawa-station-uuid',
|
||||
seatClassId: 'seat-class-uuid',
|
||||
bookingType: 'TRANSIT',
|
||||
leg2ScheduleId: 'leg2-schedule-uuid',
|
||||
leg2HoldId: 'leg2-hold-uuid',
|
||||
transitStationId: 'diredawa-station-uuid',
|
||||
leg2DestinationStationId: 'djibouti-station-uuid',
|
||||
displayCurrency: 'ETB',
|
||||
passengers: [{
|
||||
seatId: 'leg1-seat-uuid',
|
||||
leg2SeatId: 'leg2-seat-uuid',
|
||||
passengerName: 'Abebe Kebede',
|
||||
dateOfBirth: '1990-05-15',
|
||||
idDocumentType: 'NATIONAL_ID',
|
||||
idDocumentNumber: 'ET123456789',
|
||||
nationality: 'Ethiopian',
|
||||
}],
|
||||
},
|
||||
},
|
||||
ROUND_TRIP_TRANSIT: {
|
||||
summary: 'ROUND_TRIP_TRANSIT — round trip via connecting trains, 4 holds',
|
||||
value: {
|
||||
passengerId: 'passenger-uuid',
|
||||
scheduleId: 'ob-leg1-schedule-uuid',
|
||||
holdId: 'ob-leg1-hold-uuid',
|
||||
originStationId: 'addis-station-uuid',
|
||||
destinationStationId: 'diredawa-station-uuid',
|
||||
seatClassId: 'seat-class-uuid',
|
||||
bookingType: 'ROUND_TRIP_TRANSIT',
|
||||
leg2ScheduleId: 'ob-leg2-schedule-uuid',
|
||||
leg2HoldId: 'ob-leg2-hold-uuid',
|
||||
transitStationId: 'diredawa-station-uuid',
|
||||
leg2DestinationStationId: 'djibouti-station-uuid',
|
||||
returnScheduleId: 'ret-leg1-schedule-uuid',
|
||||
returnHoldId: 'ret-leg1-hold-uuid',
|
||||
returnOriginStationId: 'djibouti-station-uuid',
|
||||
returnDestinationStationId: 'diredawa-station-uuid',
|
||||
returnLeg2ScheduleId: 'ret-leg2-schedule-uuid',
|
||||
returnLeg2HoldId: 'ret-leg2-hold-uuid',
|
||||
returnTransitStationId: 'diredawa-station-uuid',
|
||||
returnLeg2DestinationStationId: 'addis-station-uuid',
|
||||
displayCurrency: 'ETB',
|
||||
passengers: [{
|
||||
seatId: 'ob-leg1-seat-uuid',
|
||||
leg2SeatId: 'ob-leg2-seat-uuid',
|
||||
returnSeatId: 'ret-leg1-seat-uuid',
|
||||
returnLeg2SeatId: 'ret-leg2-seat-uuid',
|
||||
passengerName: 'Abebe Kebede',
|
||||
dateOfBirth: '1990-05-15',
|
||||
idDocumentType: 'NATIONAL_ID',
|
||||
idDocumentNumber: 'ET123456789',
|
||||
nationality: 'Ethiopian',
|
||||
}],
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@ApiResponse({ status: 201, description: 'Booking created with fare breakdown' })
|
||||
@ApiResponse({ status: 400, description: 'Verifayda verification failed or invalid passenger data' })
|
||||
@ApiResponse({ status: 404, description: 'Trip or seat hold not found' })
|
||||
@ApiResponse({ status: 400, description: 'Missing required fields for bookingType, or Verifayda verification failed' })
|
||||
@ApiResponse({ status: 404, description: 'Schedule or seat hold not found' })
|
||||
create(@Body() dto: CreateBookingDto) {
|
||||
return this.service.create(dto);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { Currency, IdDocumentType } from '@prisma/client';
|
||||
|
||||
export class PassengerInputDto {
|
||||
@ApiProperty() @IsString() seatId: string;
|
||||
@ApiPropertyOptional({ description: '**TRANSIT / ROUND_TRIP_TRANSIT:** Seat ID on leg-2 schedule' }) @IsOptional() @IsString() leg2SeatId?: string;
|
||||
@ApiProperty({ example: 'seat-uuid', description: 'Outbound / leg-1 seat ID (all booking types)' }) @IsString() seatId: string;
|
||||
@ApiPropertyOptional({ example: 'leg2-seat-uuid', description: '**TRANSIT / ROUND_TRIP_TRANSIT:** Outbound leg-2 seat ID' }) @IsOptional() @IsString() leg2SeatId?: string;
|
||||
@ApiPropertyOptional({ example: 'return-seat-uuid', description: '**ROUND_TRIP / ROUND_TRIP_TRANSIT:** Return leg-1 seat ID' }) @IsOptional() @IsString() returnSeatId?: string;
|
||||
@ApiPropertyOptional({ example: 'ret-leg2-seat-uuid', description: '**ROUND_TRIP_TRANSIT:** Return leg-2 seat ID' }) @IsOptional() @IsString() returnLeg2SeatId?: string;
|
||||
@ApiProperty({ example: 'Abebe Kebede' }) @IsString() passengerName: string;
|
||||
@ApiProperty({ example: '1990-05-15', description: 'Date of birth (YYYY-MM-DD) for age calculation. Age <5 = CHILD (first free), Age ≥5 = ADULT (full fare)' }) @IsDateString() dateOfBirth: string;
|
||||
@ApiProperty({ example: 'NATIONAL_ID', enum: IdDocumentType, description: 'NATIONAL_ID for Ethiopians (Verifayda verified), PASSPORT for others' }) @IsEnum(IdDocumentType) idDocumentType: IdDocumentType;
|
||||
@@ -76,16 +78,16 @@ export class CreateBookingDto {
|
||||
@ApiProperty({ description: 'Passenger ID' })
|
||||
@IsString() passengerId: string;
|
||||
|
||||
@ApiProperty({ description: 'Outbound schedule ID' })
|
||||
@ApiProperty({ description: 'Outbound / leg-1 schedule ID' })
|
||||
@IsString() scheduleId: string;
|
||||
|
||||
@ApiProperty({ description: 'Outbound seat hold ID' })
|
||||
@ApiProperty({ description: 'Outbound / leg-1 seat hold ID' })
|
||||
@IsString() holdId: string;
|
||||
|
||||
@ApiProperty({ example: 'station-uuid', description: 'Outbound origin station UUID (must match the hold)' })
|
||||
@ApiProperty({ example: 'station-uuid', description: 'Outbound origin station UUID' })
|
||||
@IsString() originStationId: string;
|
||||
|
||||
@ApiProperty({ example: 'station-uuid', description: 'Outbound destination station UUID (must match the hold)' })
|
||||
@ApiProperty({ example: 'station-uuid', description: 'Outbound destination station UUID' })
|
||||
@IsString() destinationStationId: string;
|
||||
|
||||
@ApiProperty({ example: 'seat-class-uuid', description: 'Outbound seat class UUID (Economy Regular, Economy Bed, VIP Bed)' })
|
||||
@@ -94,14 +96,32 @@ export class CreateBookingDto {
|
||||
@ApiProperty({
|
||||
example: 'ONE_WAY',
|
||||
enum: ['ONE_WAY', 'ROUND_TRIP', 'TRANSIT', 'ROUND_TRIP_TRANSIT'],
|
||||
description: `Booking type:\n\n**ONE_WAY:** Single journey\n\n**ROUND_TRIP:** Outbound + return, single PNR\n\n**TRANSIT:** Single journey via connecting train, single PNR, single ticket\n\n**ROUND_TRIP_TRANSIT:** Round trip where one or both directions use a connecting train`,
|
||||
description: `Booking type:
|
||||
|
||||
**ONE_WAY:** Single direct journey — needs: scheduleId, holdId. Passenger: seatId.
|
||||
|
||||
**ROUND_TRIP:** Outbound + return, single PNR — needs above + returnScheduleId, returnHoldId, returnOriginStationId, returnDestinationStationId. Passenger: seatId + returnSeatId.
|
||||
|
||||
**TRANSIT:** Single journey via connecting train, single PNR — needs above + leg2ScheduleId, leg2HoldId, transitStationId, leg2DestinationStationId. Passenger: seatId + leg2SeatId.
|
||||
|
||||
**ROUND_TRIP_TRANSIT:** Round trip via connecting trains — needs all 4 hold sets + all station fields. Passenger: seatId + leg2SeatId + returnSeatId + returnLeg2SeatId.`,
|
||||
default: 'ONE_WAY'
|
||||
})
|
||||
@IsOptional() @IsString() bookingType?: string;
|
||||
|
||||
@ApiProperty({
|
||||
type: [PassengerInputDto],
|
||||
description: `Passenger array - type depends on bookingType:\n\n**For ONE_WAY:** PassengerInputDto[]\n- Each passenger has: seatId, passengerName, dateOfBirth, etc.\n\n**For ROUND_TRIP:** RoundTripPassengerDto[]\n- Each passenger has: outboundSeatId, returnSeatId, passengerName, dateOfBirth, etc.\n\n**Age-based pricing:** First child (<5 years) travels FREE, subsequent children pay full fare`
|
||||
description: `Passenger array — required seat fields vary by bookingType:
|
||||
|
||||
**ONE_WAY:** { seatId, passengerName, dateOfBirth, idDocumentType, … }
|
||||
|
||||
**ROUND_TRIP:** { seatId (outbound leg-1), returnSeatId (return leg-1), passengerName, … }
|
||||
|
||||
**TRANSIT:** { seatId (leg-1), leg2SeatId (leg-2), passengerName, … }
|
||||
|
||||
**ROUND_TRIP_TRANSIT:** { seatId, leg2SeatId, returnSeatId, returnLeg2SeatId, passengerName, … }
|
||||
|
||||
**Age-based pricing:** First child (<5 years) travels FREE, subsequent children pay full fare.`
|
||||
})
|
||||
@IsArray() @ValidateNested({ each: true }) @Type(() => PassengerInputDto)
|
||||
passengers: PassengerInputDto[];
|
||||
|
||||
@@ -4,16 +4,16 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { Currency, IdDocumentType } from '@prisma/client';
|
||||
|
||||
export class GuestPassengerDto {
|
||||
@ApiProperty({ example: 'seat-id-uuid', description: 'Outbound seat ID (or only seat for ONE_WAY)' })
|
||||
@ApiProperty({ example: 'seat-uuid', description: 'Outbound / leg-1 seat ID (all booking types)' })
|
||||
@IsString() seatId: string;
|
||||
|
||||
@ApiPropertyOptional({ example: 'seat-id-uuid', description: 'Return seat ID (ROUND_TRIP / ROUND_TRIP_TRANSIT outbound leg-1)' })
|
||||
@ApiPropertyOptional({ example: 'return-seat-uuid', description: '**ROUND_TRIP / ROUND_TRIP_TRANSIT:** Return leg-1 seat ID. Required for ROUND_TRIP and ROUND_TRIP_TRANSIT.' })
|
||||
@IsOptional() @IsString() returnSeatId?: string;
|
||||
|
||||
@ApiPropertyOptional({ example: 'seat-id-uuid', description: 'Leg-2 seat ID (TRANSIT / ROUND_TRIP_TRANSIT outbound leg-2)' })
|
||||
@ApiPropertyOptional({ example: 'leg2-seat-uuid', description: '**TRANSIT / ROUND_TRIP_TRANSIT:** Outbound leg-2 seat ID. Required for TRANSIT and ROUND_TRIP_TRANSIT.' })
|
||||
@IsOptional() @IsString() leg2SeatId?: string;
|
||||
|
||||
@ApiPropertyOptional({ example: 'seat-id-uuid', description: 'ROUND_TRIP_TRANSIT: return journey leg-2 seat ID' })
|
||||
@ApiPropertyOptional({ example: 'ret-leg2-seat-uuid', description: '**ROUND_TRIP_TRANSIT:** Return leg-2 seat ID. Required for ROUND_TRIP_TRANSIT.' })
|
||||
@IsOptional() @IsString() returnLeg2SeatId?: string;
|
||||
|
||||
@ApiProperty({ example: 'Abebe Kebede' })
|
||||
@@ -45,7 +45,16 @@ export class GuestPassengerDto {
|
||||
}
|
||||
|
||||
export class CreateGuestBookingDto {
|
||||
@ApiPropertyOptional({ example: 'ONE_WAY', enum: ['ONE_WAY', 'ROUND_TRIP', 'TRANSIT', 'ROUND_TRIP_TRANSIT'], default: 'ONE_WAY' })
|
||||
@ApiPropertyOptional({
|
||||
example: 'ONE_WAY',
|
||||
enum: ['ONE_WAY', 'ROUND_TRIP', 'TRANSIT', 'ROUND_TRIP_TRANSIT'],
|
||||
default: 'ONE_WAY',
|
||||
description: `Booking type:
|
||||
**ONE_WAY:** scheduleId + holdId. Passenger: seatId.
|
||||
**ROUND_TRIP:** above + returnScheduleId/returnHoldId/returnOriginStationId/returnDestinationStationId. Passenger: seatId + returnSeatId.
|
||||
**TRANSIT:** above + leg2ScheduleId/leg2HoldId/transitStationId/leg2DestinationStationId. Passenger: seatId + leg2SeatId.
|
||||
**ROUND_TRIP_TRANSIT:** all 4 hold sets + all station fields. Passenger: seatId + leg2SeatId + returnSeatId + returnLeg2SeatId.`
|
||||
})
|
||||
@IsOptional() @IsString() bookingType?: 'ONE_WAY' | 'ROUND_TRIP' | 'TRANSIT' | 'ROUND_TRIP_TRANSIT';
|
||||
|
||||
@ApiProperty({ example: 'schedule-uuid', description: 'Outbound / leg-1 schedule UUID' })
|
||||
@@ -108,7 +117,14 @@ export class CreateGuestBookingDto {
|
||||
@ApiPropertyOptional({ example: 'seat-class-uuid', description: 'ROUND_TRIP_TRANSIT: return leg-2 seat class UUID' })
|
||||
@IsOptional() @IsString() returnLeg2SeatClassId?: string;
|
||||
|
||||
@ApiProperty({ type: [GuestPassengerDto], description: 'Array of passengers. For ROUND_TRIP each passenger must include returnSeatId.' })
|
||||
@ApiProperty({
|
||||
type: [GuestPassengerDto],
|
||||
description: `Passenger array. Required seat fields vary by bookingType:
|
||||
- ONE_WAY: seatId
|
||||
- ROUND_TRIP: seatId + returnSeatId
|
||||
- TRANSIT: seatId + leg2SeatId
|
||||
- ROUND_TRIP_TRANSIT: seatId + leg2SeatId + returnSeatId + returnLeg2SeatId`
|
||||
})
|
||||
@IsArray() @ValidateNested({ each: true }) @Type(() => GuestPassengerDto) passengers: GuestPassengerDto[];
|
||||
|
||||
@ApiPropertyOptional({ example: 'WEEKEND15' })
|
||||
|
||||
Reference in New Issue
Block a user