mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 03:10:54 +00:00
110 lines
5.1 KiB
TypeScript
110 lines
5.1 KiB
TypeScript
import { Body, Controller, Post, Get, Query } from '@nestjs/common';
|
||
import { ApiTags, ApiOperation, ApiResponse, ApiQuery } from '@nestjs/swagger';
|
||
import { IsPublic } from '@tria-plc/api-common/modules/auth/decorators/public.decorator';
|
||
import { SearchService } from './search.service';
|
||
import { SearchTripsDto, FareQuoteDto, FareBreakdownRequestDto, AvailableDatesQueryDto } from './search.dto';
|
||
|
||
@ApiTags('Search')
|
||
@Controller('search')
|
||
@IsPublic()
|
||
export class SearchController {
|
||
constructor(private service: SearchService) {}
|
||
|
||
@Post()
|
||
@ApiOperation({
|
||
summary: 'Search trips by origin, destination, date, passengers, and nationality',
|
||
description: `Finds all train schedules matching search criteria with real-time seat availability and coach type options.
|
||
|
||
**Coach Type Selection Flow:**
|
||
- Users browse available coach types (Economy, VIP, etc.)
|
||
- Each coach type displays available seat classes and base fares
|
||
- Users select a coach type to proceed to seat selection
|
||
- At seat selection, users choose specific seat and class (actual price confirmed here)
|
||
- Final fare may adjust based on seat position/amenities selected
|
||
|
||
**Features:**
|
||
- Any origin→destination stop pair (not just terminals)
|
||
- Age-based passenger counts (adults ≥5 years, children <5 years)
|
||
- Nationality filtering (Ethiopian, Djiboutian, Other)
|
||
- Real-time seat availability per class
|
||
- Multi-currency fare display
|
||
- Segment-based availability (seat booked A→B still available B→D)`
|
||
})
|
||
@ApiResponse({ status: 200, description: 'Matching schedules with coachTypes array showing available coach types with seat classes and base fares' })
|
||
searchTrips(@Body() dto: SearchTripsDto) {
|
||
return this.service.searchTrips(dto);
|
||
}
|
||
|
||
@Post('fare-quote')
|
||
@ApiOperation({
|
||
summary: 'Get fare quote with age-based pricing and multi-currency support',
|
||
description: `Calculates detailed fare breakdown for a specific schedule leg.
|
||
|
||
Age-Based Pricing:
|
||
- ADULT (≥5 years): 100% of base fare
|
||
- CHILD (<5 years): First child FREE, subsequent children 100%
|
||
- Example: 2 adults + 3 children = 4× base fare
|
||
|
||
Pricing Rules (priority order):
|
||
1. Schedule-scoped FareRule (tripId = scheduleId)
|
||
2. Segment route FareRule (e.g. ADD-DRE)
|
||
3. Full-route FareRule (e.g. ADD-DJI)
|
||
4. Default hardcoded fare
|
||
|
||
Multi-Currency:
|
||
- Transaction currency: ETB
|
||
- Display currencies: ETB, DJF, USD
|
||
- Real-time exchange rate conversion
|
||
|
||
Nationality-Based:
|
||
- Ethiopian: National ID verification required
|
||
- Djiboutian: Passport details, Waafi payment available
|
||
- Other: Passport details, international payments`
|
||
})
|
||
@ApiResponse({ status: 200, description: 'Fare breakdown with adult/child pricing, discounts, taxes, and currency conversion' })
|
||
@ApiResponse({ status: 404, description: 'Schedule not found or origin/destination not on schedule' })
|
||
getFareQuote(@Body() dto: FareQuoteDto) {
|
||
return this.service.getFareQuote(dto);
|
||
}
|
||
|
||
@Get('available-dates')
|
||
@ApiOperation({
|
||
summary: 'Which dates in a range have a bookable schedule for an origin/destination pair',
|
||
description: `Used to disable schedule-less dates on the search date picker before the user submits a search.
|
||
|
||
For each date in the (server-clamped, max 90-day) range, a date is "available" if at least one
|
||
schedule exists for the origin→destination pair whose status/package/coach state is bookable and
|
||
whose check-in cutoff has not yet passed. This does not check seat-level availability — a date
|
||
can be marked available and still turn out fully booked when actually searched.`,
|
||
})
|
||
@ApiResponse({ status: 200, description: 'routeExists flag plus a per-date availability list' })
|
||
getAvailableDates(@Query() dto: AvailableDatesQueryDto) {
|
||
return this.service.getAvailableDates(dto);
|
||
}
|
||
|
||
@Get('fare-breakdown')
|
||
@ApiOperation({
|
||
summary: 'Per-passenger fare breakdown for booking review page',
|
||
description: `Calculates a line-item fare for each individual passenger based on their date of birth, nationality, and chosen seat class.
|
||
|
||
- Age is derived from dateOfBirth at request time (ADULT ≥5 yrs, CHILD <5 yrs)
|
||
- First CHILD in the list travels free (pays only premium + insurance fees)
|
||
- Each passenger can have a different seat class and nationality
|
||
- Returns per-passenger lines plus subtotal, discount, and grand total
|
||
|
||
**passengers** must be a URL-encoded JSON array, e.g.:
|
||
\`[{"passengerName":"Abebe","dateOfBirth":"1985-03-15","seatClassId":"uuid","nationality":"Ethiopian"}]\``,
|
||
})
|
||
@ApiQuery({ name: 'scheduleId', description: 'TrainSchedule UUID' })
|
||
@ApiQuery({ name: 'originStationId', description: 'Origin station UUID' })
|
||
@ApiQuery({ name: 'destinationStationId', description: 'Destination station UUID' })
|
||
@ApiQuery({ name: 'passengers', description: 'URL-encoded JSON array of passengers: [{passengerName, dateOfBirth, seatClassId, nationality?}]' })
|
||
@ApiQuery({ name: 'promoCode', required: false })
|
||
@ApiQuery({ name: 'displayCurrency', required: false, enum: ['ETB', 'DJF', 'USD'] })
|
||
@ApiResponse({ status: 200, description: 'Per-passenger fare lines with grand total' })
|
||
@ApiResponse({ status: 404, description: 'Schedule not found' })
|
||
getFareBreakdown(@Query() dto: FareBreakdownRequestDto) {
|
||
return this.service.getFareBreakdown(dto);
|
||
}
|
||
}
|