Files
edr-platform/apps/edr-freight-api/src/modules/import-operations/import-operations.controller.ts
Hagernesh b4f2ec1c75 feat(train-scheduling): load returned empties onto an export train
Empties had no way onto a departure: the return record could name a train
but nothing seated it on a wagon. Export schedules now expose a loading
action that packs selected returns onto free wagons at one 40ft or two 20ft
each, enforced both in the picker and in the API (existing empties on the
schedule count against their wagon).

Adds container_size, train_schedule_id and wagon_sequence_no to
freight.empty_container_returns.
2026-08-14 09:57:50 +00:00

125 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Body, Controller, Get, Param, ParseUUIDPipe, Post, Query } from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { BookingStaff } from '../../common/booking-guards';
import { FREIGHT_PERMS } from '../../seed/freight-permissions.registry';
import {
AssignCustomsRiskDto,
CreateDjiboutiIncidentDto,
CreateEmptyContainerReturnDto,
ImportOperationActionDto,
LoadEmptyContainersOnTrainDto,
RecordDeclarationDto,
UpdateEmptyContainerReturnStatusDto,
UploadImportCustomsDocumentDto,
} from './dto/import-operations.dto';
import { ImportOperationsService } from './import-operations.service';
@ApiTags('import-operations')
@ApiBearerAuth()
@Controller('import-operations')
// Post-booking customs / import-operations actions are GL/Ops work, mirroring the
// contracts controller's GL operational endpoints (risk, duty, milestones).
@BookingStaff(FREIGHT_PERMS.bookings.operations)
export class ImportOperationsController {
constructor(private readonly service: ImportOperationsService) {}
@Get('djibouti-incidents')
@ApiOperation({ summary: 'Batch 8: list Djibouti import incidents' })
listIncidents(@Query('bookingId') bookingId?: string) {
return this.service.listIncidents(bookingId);
}
@Post('djibouti-incidents')
@ApiOperation({ summary: 'Batch 8: report a Djibouti import incident / exception' })
createIncident(@Body() dto: CreateDjiboutiIncidentDto) {
return this.service.createIncident(dto);
}
@Get('customs/:bookingId')
@ApiOperation({ summary: 'Batch 12: import customs finalization state' })
getCustoms(@Param('bookingId', ParseUUIDPipe) bookingId: string) {
return this.service.getCustoms(bookingId);
}
@Post('customs/:bookingId/documents')
@ApiOperation({ summary: 'Batch 12: upload IM4/IM5/T1/permit/payment-slip documents' })
uploadCustomsDocument(
@Param('bookingId', ParseUUIDPipe) bookingId: string,
@Body() dto: UploadImportCustomsDocumentDto,
) {
return this.service.uploadCustomsDocument(bookingId, dto);
}
@Post('customs/:bookingId/declaration')
@ApiOperation({ summary: 'Batch 12: record declaration serial number' })
recordDeclaration(
@Param('bookingId', ParseUUIDPipe) bookingId: string,
@Body() dto: RecordDeclarationDto,
) {
return this.service.recordDeclaration(bookingId, dto);
}
@Post('customs/:bookingId/notify-duties-taxes')
@ApiOperation({ summary: 'Batch 12: notify duties and taxes' })
notifyDutiesTaxes(
@Param('bookingId', ParseUUIDPipe) bookingId: string,
@Body() dto: ImportOperationActionDto,
) {
return this.service.notifyDutiesTaxes(bookingId, dto);
}
@Post('customs/:bookingId/duties-taxes-paid')
@ApiOperation({ summary: 'Batch 12: mark duties and taxes paid' })
markDutiesTaxesPaid(
@Param('bookingId', ParseUUIDPipe) bookingId: string,
@Body() dto: ImportOperationActionDto,
) {
return this.service.markDutiesTaxesPaid(bookingId, dto);
}
@Post('customs/:bookingId/risk')
@ApiOperation({ summary: 'Batch 12: assign customs risk' })
assignRisk(@Param('bookingId', ParseUUIDPipe) bookingId: string, @Body() dto: AssignCustomsRiskDto) {
return this.service.assignRisk(bookingId, dto);
}
@Post('customs/:bookingId/release-permitted')
@ApiOperation({ summary: 'Batch 12: mark import release permitted' })
markReleasePermitted(
@Param('bookingId', ParseUUIDPipe) bookingId: string,
@Body() dto: ImportOperationActionDto,
) {
return this.service.markReleasePermitted(bookingId, dto);
}
@Get('empty-container-returns')
@ApiOperation({ summary: 'Batch 16: list empty container returns' })
listEmptyReturns() {
return this.service.listEmptyReturns();
}
@Post('empty-container-returns')
@ApiOperation({ summary: 'Batch 16: create an empty container return record' })
createEmptyReturn(@Body() dto: CreateEmptyContainerReturnDto) {
return this.service.createEmptyReturn(dto);
}
@Post('empty-container-returns/load-on-train')
@ApiOperation({
summary: 'Load returned empties onto an export train (1×40ft or 2×20ft per wagon)',
})
loadEmptyReturnsOnTrain(@Body() dto: LoadEmptyContainersOnTrainDto) {
return this.service.loadEmptyReturnsOnTrain(dto);
}
@Post('empty-container-returns/:id/status')
@ApiOperation({ summary: 'Batch 16: advance empty container return workflow' })
updateEmptyReturnStatus(
@Param('id', ParseUUIDPipe) id: string,
@Body() dto: UpdateEmptyContainerReturnStatusDto,
) {
return this.service.updateEmptyReturnStatus(id, dto);
}
}