Files
edr-platform/apps/edr-freight-api/src/modules/shipping-lines/shipping-line-booking-completion.controller.ts
2026-08-13 18:56:52 +00:00

110 lines
3.6 KiB
TypeScript

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);
}
}