mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 04:20:55 +00:00
183 lines
6.5 KiB
TypeScript
183 lines
6.5 KiB
TypeScript
import {
|
|
Body,
|
|
Controller,
|
|
Delete,
|
|
Get,
|
|
HttpCode,
|
|
Param,
|
|
ParseUUIDPipe,
|
|
Patch,
|
|
Post,
|
|
Query,
|
|
Request,
|
|
UploadedFiles,
|
|
UseInterceptors,
|
|
} from "@nestjs/common";
|
|
import { AnyFilesInterceptor } from "@nestjs/platform-express";
|
|
import {
|
|
ApiBearerAuth,
|
|
ApiBody,
|
|
ApiConsumes,
|
|
ApiOperation,
|
|
ApiTags,
|
|
} from "@nestjs/swagger";
|
|
|
|
import { BookingsService } from "./bookings.service";
|
|
import { CreateBookingDto } from "./dto/create-booking.dto";
|
|
import { FilterBookingDto } from "./dto/filter-booking.dto";
|
|
import { UpdateBookingDto } from "./dto/update-booking.dto";
|
|
import { UpdateStatusDto } from "./dto/update-status.dto";
|
|
|
|
@ApiTags("bookings")
|
|
@Controller("bookings")
|
|
@ApiBearerAuth()
|
|
export class BookingsController {
|
|
constructor(private readonly bookingsService: BookingsService) { }
|
|
|
|
// ── 1. Create booking (multipart/form-data) ──────────────────────────
|
|
@Post()
|
|
@UseInterceptors(AnyFilesInterceptor())
|
|
@ApiConsumes("multipart/form-data")
|
|
@ApiOperation({
|
|
summary: "Create a new freight booking",
|
|
description:
|
|
"Accepts all booking fields as form fields + dynamic file keys (e.g. passport, license). " +
|
|
"Auto-enables consolidation when containerType=20FT and odd quantity.",
|
|
})
|
|
@ApiBody({
|
|
description:
|
|
"Booking form data. Attach files with any field name (e.g. passport, tin_certificate). " +
|
|
"Each uploaded file is saved as a row in the files table (resource=bookings).",
|
|
type: CreateBookingDto,
|
|
})
|
|
create(
|
|
@Body() dto: CreateBookingDto,
|
|
@UploadedFiles() files: Express.Multer.File[],
|
|
@Request() req: any,
|
|
) {
|
|
console.log(
|
|
"[BookingsController] Files received:",
|
|
files?.length,
|
|
files?.map((f) => ({
|
|
fieldname: f.fieldname,
|
|
originalname: f.originalname,
|
|
size: f.size,
|
|
mimetype: f.mimetype,
|
|
})),
|
|
);
|
|
const userId: string | undefined = req.user?.id ?? req.user?.sub;
|
|
return this.bookingsService.create(dto, files ?? [], userId);
|
|
}
|
|
|
|
// ── 2. Update draft booking (multipart/form-data) ─────────────────────
|
|
@Patch(":id")
|
|
@UseInterceptors(AnyFilesInterceptor())
|
|
@ApiConsumes("multipart/form-data")
|
|
@ApiOperation({
|
|
summary: "Update a draft booking",
|
|
description:
|
|
"Only DRAFT bookings can be updated. New files are merged into existing documents.",
|
|
})
|
|
@ApiBody({ type: UpdateBookingDto })
|
|
update(
|
|
@Param("id", ParseUUIDPipe) id: string,
|
|
@Body() dto: UpdateBookingDto,
|
|
@UploadedFiles() files: Express.Multer.File[],
|
|
) {
|
|
return this.bookingsService.update(id, dto, files ?? []);
|
|
}
|
|
|
|
// ── 3. List bookings (paginated + filtered) ───────────────────────────
|
|
@Get()
|
|
@ApiOperation({
|
|
summary: "List freight bookings (paginated)",
|
|
description:
|
|
"Filter by status, customerId, contractType, serviceType, tradeDirection, " +
|
|
"paymentCurrency, freightType, containerType, allowConsolidation, consolidationPaired. " +
|
|
"Sort by createdAt or priorityScore.",
|
|
})
|
|
findAll(@Query() filter: FilterBookingDto) {
|
|
return this.bookingsService.findAll(filter);
|
|
}
|
|
|
|
// ── 5. Lookup by reference (must be before :id to avoid conflict) ─────
|
|
@Get("by-reference/:reference")
|
|
@ApiOperation({
|
|
summary: "Get a freight booking by reference number",
|
|
description: "Lookup booking by its human-readable reference string.",
|
|
})
|
|
findByReference(@Param("reference") reference: string) {
|
|
return this.bookingsService.findByReference(reference);
|
|
}
|
|
|
|
// ── 4. Get single booking by ID ───────────────────────────────────────
|
|
@Get(":id")
|
|
@ApiOperation({ summary: "Get a freight booking by ID" })
|
|
findOne(@Param("id", ParseUUIDPipe) id: string) {
|
|
return this.bookingsService.findById(id);
|
|
}
|
|
|
|
// ── 6. Soft-delete (DRAFT only) ───────────────────────────────────────
|
|
@Delete(":id")
|
|
@HttpCode(204)
|
|
@ApiOperation({
|
|
summary: "Soft-delete a freight booking",
|
|
description: "Only DRAFT bookings can be deleted.",
|
|
})
|
|
remove(@Param("id", ParseUUIDPipe) id: string) {
|
|
return this.bookingsService.remove(id);
|
|
}
|
|
|
|
// ── 7. Unified status transition ──────────────────────────────────────
|
|
@Patch(":id/status")
|
|
@ApiOperation({
|
|
summary: "Transition booking status",
|
|
description:
|
|
"Unified endpoint for all status transitions. Actions: " +
|
|
"SUBMIT, APPROVE_STAFF, APPROVE_DIRECTOR, APPROVE_CEO, REJECT, CANCEL, ACTIVATE, EXPIRE. " +
|
|
"Approval routing: Standard → LINE_STAFF → DIRECTOR → SIGNED. " +
|
|
"Bulk/high-volume → DIRECTOR → CEO → SIGNED.",
|
|
})
|
|
updateStatus(
|
|
@Param("id", ParseUUIDPipe) id: string,
|
|
@Body() dto: UpdateStatusDto,
|
|
) {
|
|
return this.bookingsService.updateStatus(id, dto);
|
|
}
|
|
|
|
// ── 8. Request or auto-pair consolidation ─────────────────────────────
|
|
@Post(":id/consolidation")
|
|
@ApiOperation({
|
|
summary: "Request freight consolidation",
|
|
description:
|
|
"Searches for a compatible 20FT partner (same origin, destination, tradeDirection). " +
|
|
"If a partner is found, both bookings are paired. If not, the booking enters the consolidation queue.",
|
|
})
|
|
requestConsolidation(@Param("id", ParseUUIDPipe) id: string) {
|
|
return this.bookingsService.requestConsolidation(id);
|
|
}
|
|
|
|
// ── 9. Remove consolidation pairing ───────────────────────────────────
|
|
@Delete(":id/consolidation")
|
|
@ApiOperation({
|
|
summary: "Remove consolidation pairing",
|
|
description:
|
|
"Unpairs both bookings and returns them to PENDING_CONSOLIDATION status.",
|
|
})
|
|
removeConsolidation(@Param("id", ParseUUIDPipe) id: string) {
|
|
return this.bookingsService.removeConsolidation(id);
|
|
}
|
|
|
|
// ── 10. Get consolidation details ─────────────────────────────────────
|
|
@Get(":id/consolidation")
|
|
@ApiOperation({
|
|
summary: "Get consolidation details",
|
|
description:
|
|
"Returns partner booking details and split billing information.",
|
|
})
|
|
getConsolidationDetails(@Param("id", ParseUUIDPipe) id: string) {
|
|
return this.bookingsService.getConsolidationDetails(id);
|
|
}
|
|
|
|
}
|