import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, ParseUUIDPipe, Patch, Post, Query, } from '@nestjs/common'; import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; import { TrainSchedulingManage, TrainSchedulingView } from '../../common/booking-guards'; import { CreateLastMileDto } from './dto/create-last-mile.dto'; import { UpdateLastMileDto } from './dto/update-last-mile.dto'; import { AllocateLastMileContainersDto } from './dto/allocate-containers.dto'; import { SetVehiclesDto } from './dto/set-vehicles.dto'; import { LastMileStatus } from './entities/last-mile.entity'; import { LastMileService } from './last-mile.service'; import { LastMileInvoiceService } from './last-mile-invoice.service'; import { Freight } from '@edr/types'; import { BillingService } from '../billing/billing.service'; import { BookingsService } from '../bookings/bookings.service'; @ApiTags('last-mile') @ApiBearerAuth() @Controller('last-mile') @TrainSchedulingView() export class LastMileController { constructor( private readonly lastMileService: LastMileService, private readonly lastMileInvoiceService: LastMileInvoiceService, private readonly billingService: BillingService, private readonly bookingsService: BookingsService ) {} @Get() @ApiOperation({ summary: 'List last-mile legs' }) findAll( @Query('status') status?: string, @Query('bookingId') bookingId?: string, @Query('vehicleId') vehicleId?: string, @Query('page') page?: string, @Query('pageSize') pageSize?: string, @Query('sortBy') sortBy?: string, @Query('sortOrder') sortOrder?: 'ASC' | 'DESC', ) { return this.lastMileService.findAll({ status: status as LastMileStatus | undefined, bookingId, vehicleId, page: page ? parseInt(page, 10) : undefined, pageSize: pageSize ? parseInt(pageSize, 10) : undefined, sortBy, sortOrder, }); } @Get(':id') @ApiOperation({ summary: 'Get a last-mile leg by ID' }) findOne(@Param('id', ParseUUIDPipe) id: string) { return this.lastMileService.findById(id); } @Post('accept/:reference') @TrainSchedulingManage() @ApiOperation({ summary: 'Accept a paid booking and create a last-mile leg' }) acceptBooking(@Param('reference') reference: string) { return this.lastMileService.acceptBookingByReference(reference); } @Post() @TrainSchedulingManage() @ApiOperation({ summary: 'Create a last-mile leg' }) create(@Body() dto: CreateLastMileDto) { return this.lastMileService.create(dto); } @Patch(':id') @TrainSchedulingManage() @ApiOperation({ summary: 'Update a last-mile leg' }) async update(@Param('id', ParseUUIDPipe) id: string, @Body() dto: UpdateLastMileDto) { const record = await this.lastMileService.update(id, dto); // Auto-generate invoice if distance or payment was updated const booking = await this.bookingsService.findById(record.bookingId); const currency = booking.paymentCurrency || "ETB"; if (dto.exactKm !== undefined || dto.exactKm != record.exactKm || dto.remainingPayment !== undefined || dto.remainingPayment !== record.remainingPayment) { await this.billingService.generateInvoice({ source: Freight.InvoiceSource.LastMile, sourceId: record.id, type: "LAST_MILE", companyId: booking.companyId, companyProfileId: booking.companyProfileId, currency, lines: [ { chargeType: "LAST_MILE", description: "Last Mile Transportation Service", quantity: 1, unitRate: record.remainingPayment, amount: record.remainingPayment, currency, }, ], subtotalAmount: record.remainingPayment, taxAmount: 0, // Replace if VAT/tax applies totalAmount: record.remainingPayment, dueInDays: 7, status: Freight.InvoiceStatus.Pending, }); await this.lastMileInvoiceService.ensureInvoiceFor(record); } return record; } @Delete(':id') @TrainSchedulingManage() @HttpCode(HttpStatus.NO_CONTENT) @ApiOperation({ summary: 'Soft-delete a last-mile leg' }) remove(@Param('id', ParseUUIDPipe) id: string) { return this.lastMileService.remove(id); } @Post(':id/allocate-containers') @TrainSchedulingManage() @ApiOperation({ summary: 'Allocate containers to vehicles' }) async allocateContainers( @Param('id', ParseUUIDPipe) id: string, @Body() dto: AllocateLastMileContainersDto, ) { return this.lastMileService.allocateContainers(id, dto.allocations); } @Post(':id/vehicles') @TrainSchedulingManage() @ApiOperation({ summary: 'Set the vehicles assigned to a last-mile delivery (multi-truck)' }) async setVehicles( @Param('id', ParseUUIDPipe) id: string, @Body() dto: SetVehiclesDto, ) { return this.lastMileService.setVehicles(id, dto.vehicleIds); } }