Files
edr-platform/apps/edr-freight-api/src/modules/shipping-lines/shipping-line-bookings.controller.ts
Marshal 0e00a98ef3 feat: implement shipping line booking completion functionality
- Added ShippingLineBookingCompletionController and associated service to handle the completion of shipping line bookings.
- Introduced a new module for booking completion to maintain module separation and avoid cyclic dependencies.
- Updated the train scheduling global rules to set default desk hours to 24 hours.
- Modified existing services and entities to accommodate the new booking completion logic.
- Enhanced the front-end components to support the new booking completion flow, including updates to the booking detail and bookings pages.
- Implemented validation and error handling for booking completion, ensuring that only approved bookings can be completed.
- Added migration to set default desk hours in the database.
2026-08-13 15:10:27 +00:00

108 lines
3.4 KiB
TypeScript

import { CurrentUser } from "@edr/api-common";
import {
Body,
Controller,
Get,
Param,
ParseUUIDPipe,
Post,
} from "@nestjs/common";
import { ApiBearerAuth, ApiOperation, ApiTags } from "@nestjs/swagger";
import { PortalCustomer } from "../../common/booking-guards";
import { CancelShippingLineBookingDto } from "./dto/cancel-shipping-line-booking.dto";
import { InitiateShippingLineBookingDto } from "./dto/initiate-shipping-line-booking.dto";
import { ShippingLineBookingsService } from "./shipping-line-bookings.service";
interface CurrentIamUser {
id: string;
}
/**
* Bookings a shipping line makes for itself, from the portal.
*
* Separate from `/bookings` (customers) on purpose — see
* {@link ShippingLineBookingsService} for why the two flows are not merged.
* `PortalCustomer` only proves a valid portal session; the service resolves the
* shipping-line account from that session and rejects anyone else, so the owner
* is never taken from the request body.
*/
@ApiTags("shipping-line-bookings")
@Controller("shipping-line-bookings")
@ApiBearerAuth()
export class ShippingLineBookingsController {
constructor(
private readonly shippingLineBookingsService: ShippingLineBookingsService,
) {}
@Post("initiate")
@PortalCustomer()
@ApiOperation({
summary:
"Initiate a bare booking (no contract). Starts at AWAITING_DOCUMENTS so the shipping line can upload its documents for Operations to approve.",
})
async initiate(
@CurrentUser() user: CurrentIamUser,
@Body() dto: InitiateShippingLineBookingDto,
) {
return this.shippingLineBookingsService.initiate(user.id, dto);
}
// Declared before @Get(":id") so the path isn't captured as a booking id.
@Get("reference-data")
@PortalCustomer()
@ApiOperation({
summary:
"Catalog for the initiate form: bookable routes (each carrying its trade direction) and service types.",
})
async referenceData(@CurrentUser() user: CurrentIamUser) {
return this.shippingLineBookingsService.referenceData(user.id);
}
@Get("my")
@PortalCustomer()
@ApiOperation({ summary: "List the signed-in shipping line's bookings." })
async listMine(@CurrentUser() user: CurrentIamUser) {
return this.shippingLineBookingsService.listMine(user.id);
}
// Declared before @Get(":id") so the path isn't captured as a booking id.
@Get("my-trains")
@PortalCustomer()
@ApiOperation({
summary:
"Train departures dedicated to the signed-in shipping line. These trains are hidden from customers; this is the only portal read that surfaces them.",
})
async listMyTrains(@CurrentUser() user: CurrentIamUser) {
return this.shippingLineBookingsService.listMyTrains(user.id);
}
@Get(":id")
@PortalCustomer()
@ApiOperation({ summary: "Get one of the signed-in shipping line's bookings." })
async findMine(
@CurrentUser() user: CurrentIamUser,
@Param("id", ParseUUIDPipe) id: string,
) {
return this.shippingLineBookingsService.findMine(user.id, id);
}
@Post(":id/cancel")
@PortalCustomer()
@ApiOperation({
summary:
"Cancel one of the signed-in shipping line's own bookings. Allowed only before the booking is priced.",
})
async cancelMine(
@CurrentUser() user: CurrentIamUser,
@Param("id", ParseUUIDPipe) id: string,
@Body() dto: CancelShippingLineBookingDto,
) {
return this.shippingLineBookingsService.cancelMine(
user.id,
id,
dto.reason,
);
}
}