import { CurrentUser } from "@edr/api-common"; import { Body, Controller, Get, Param, ParseUUIDPipe, Post, Query, } from "@nestjs/common"; import { ApiBearerAuth, ApiOperation, ApiTags } from "@nestjs/swagger"; import { PortalCustomer } from "../../common/booking-guards"; import { CompleteShippingLineBookingDto } from "./dto/complete-shipping-line-booking.dto"; import { ShippingLineBookingCompletionService } from "./shipping-line-booking-completion.service"; interface CurrentIamUser { id: string; } /** * The completion half of shipping-line bookings, sharing the * `/shipping-line-bookings` prefix with {@link ShippingLineBookingsController}. * Separate controller because it lives in its own module — see * {@link ShippingLineBookingCompletionService} for why the module split exists. */ @ApiTags("shipping-line-bookings") @Controller("shipping-line-bookings") @ApiBearerAuth() export class ShippingLineBookingCompletionController { constructor( private readonly completionService: ShippingLineBookingCompletionService, ) {} @Get(":id/available-days") @PortalCustomer() @ApiOperation({ summary: "Days with an open departure that can carry this booking's cargo — for the completion form's day picker.", }) async availableDays( @CurrentUser() user: CurrentIamUser, @Param("id", ParseUUIDPipe) id: string, ) { return this.completionService.availableDaysMine(user.id, id); } @Get(":id/trains") @PortalCustomer() @ApiOperation({ summary: "The line's dedicated trains on the booking's lane for a shipment day, each with per-wagon-type free space — for the completion form's train picker. Cargo context (sizes/cargoTypeId/wagons) refines the availability.", }) async trainsForDay( @CurrentUser() user: CurrentIamUser, @Param("id", ParseUUIDPipe) id: string, @Query("date") date?: string, @Query("sizes") sizes?: string, @Query("cargoTypeId") cargoTypeId?: string, @Query("wagons") wagons?: string, ) { return this.completionService.trainsForDayMine(user.id, id, date, { containerSizes: sizes ? sizes.split(",").filter(Boolean) : undefined, cargoTypeId: cargoTypeId || undefined, wagons: wagons ? Number(wagons) : undefined, }); } @Post(":id/price-preview") @PortalCustomer() @ApiOperation({ summary: "Authoritative price quote for the completion payload — same compute as /complete, saved as the booking's breakdown + rate snapshots (refreshed on every re-preview). Persists nothing else.", }) async pricePreview( @CurrentUser() user: CurrentIamUser, @Param("id", ParseUUIDPipe) id: string, @Body() dto: CompleteShippingLineBookingDto, ) { return this.completionService.previewPriceMine(user.id, id, dto); } @Get(":id/operations") @PortalCustomer() @ApiOperation({ summary: "Operations view of the booking: the train it rides (assigned or requested) and the wagons allocated to it.", }) async operations( @CurrentUser() user: CurrentIamUser, @Param("id", ParseUUIDPipe) id: string, ) { return this.completionService.operationsMine(user.id, id); } @Post(":id/complete") @PortalCustomer() @ApiOperation({ summary: "Complete an approved (CLEARANCE_READY) booking: cargo + binding shipment day. Prices off the line's rates, records the charge on the credit ledger and requests operation.", }) async completeMine( @CurrentUser() user: CurrentIamUser, @Param("id", ParseUUIDPipe) id: string, @Body() dto: CompleteShippingLineBookingDto, ) { return this.completionService.completeMine(user.id, id, dto); } }