src/trips/trips.controller.ts
trips
Methods |
|
| Async autoSuggest |
autoSuggest()
|
Decorators :
@Post('auto-suggest')
|
|
Defined in src/trips/trips.controller.ts:136
|
|
Returns :
unknown
|
| Async cancel |
cancel(orgId: string, id: string)
|
Decorators :
@Post(':id/cancel')
|
|
Defined in src/trips/trips.controller.ts:116
|
|
Returns :
unknown
|
| Async complete |
complete(orgId: string, id: string)
|
Decorators :
@Post(':id/complete')
|
|
Defined in src/trips/trips.controller.ts:106
|
|
Returns :
unknown
|
| Async create | |||||||||
create(orgId: string, dto: CreateTripDto)
|
|||||||||
Decorators :
@Post()
|
|||||||||
|
Defined in src/trips/trips.controller.ts:48
|
|||||||||
|
Parameters :
Returns :
unknown
|
| Async dispatch |
dispatch(orgId: string, id: string)
|
Decorators :
@Post(':id/dispatch')
|
|
Defined in src/trips/trips.controller.ts:96
|
|
Returns :
unknown
|
| Async findAll | |||||||||||||||
findAll(orgId: string, role: string, userId: string, query: PaginationParams)
|
|||||||||||||||
Decorators :
@Get()
|
|||||||||||||||
|
Defined in src/trips/trips.controller.ts:58
|
|||||||||||||||
|
Parameters :
Returns :
unknown
|
| Async findOne |
findOne(orgId: string, id: string)
|
Decorators :
@Get(':id')
|
|
Defined in src/trips/trips.controller.ts:69
|
|
Returns :
unknown
|
| Async optimizeRoute |
optimizeRoute(orgId: string, tripId: string)
|
Decorators :
@Post('optimize-route/:tripId')
|
|
Defined in src/trips/trips.controller.ts:143
|
|
Returns :
unknown
|
| Async remove |
remove(orgId: string, id: string)
|
Decorators :
@Delete(':id')
|
|
Defined in src/trips/trips.controller.ts:126
|
|
Returns :
unknown
|
| Async update | ||||||||||||||||||
update(orgId: string, userId: string, role: string, id: string, dto: UpdateTripDto)
|
||||||||||||||||||
Decorators :
@Patch(':id')
|
||||||||||||||||||
|
Defined in src/trips/trips.controller.ts:83
|
||||||||||||||||||
|
Parameters :
Returns :
unknown
|
import {
Controller,
Get,
Post,
Patch,
Delete,
Body,
Param,
Query,
UseGuards,
NotFoundException,
} from '@nestjs/common';
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
import { TripsService } from './trips.service';
import { AutoAssignService } from './optimizer/auto-assign.service';
import { RouteOptimizer } from './optimizer/route.optimizer';
import { CreateTripDto } from './dto/create-trip.dto';
import { UpdateTripDto } from './dto/update-trip.dto';
import { CombinedAuthGuard } from '../auth/guards/combined-auth.guard';
import { RolesGuard } from '../auth/guards/roles.guard';
import { Roles } from '../auth/decorators/roles.decorator';
import { CurrentUser } from '../auth/decorators/current-user.decorator';
import { ApiPagination } from '../common/decorators/api-pagination.decorator';
import { PaginationParams } from '../common/utils/pagination.util';
const TRIP_WRITERS = [
'SUPER_ADMIN',
'ADMIN',
'OPERATIONS_MANAGER',
'PLANNER',
'DISPATCHER',
] as const;
@ApiTags('Trips')
@ApiBearerAuth()
@UseGuards(CombinedAuthGuard, RolesGuard)
@Controller('trips')
export class TripsController {
constructor(
private readonly tripsService: TripsService,
private readonly autoAssignService: AutoAssignService,
private readonly routeOptimizer: RouteOptimizer,
) {}
@Post()
@Roles(...TRIP_WRITERS)
@ApiOperation({ summary: 'Create a new trip' })
async create(
@CurrentUser('organizationId') orgId: string,
@Body() dto: CreateTripDto,
) {
return this.tripsService.create(orgId, dto);
}
@Get()
@ApiPagination()
@ApiOperation({ summary: 'List all trips (drivers only see their own)' })
async findAll(
@CurrentUser('organizationId') orgId: string,
@CurrentUser('role') role: string,
@CurrentUser('id') userId: string,
@Query() query: PaginationParams,
) {
return this.tripsService.findAll(orgId, query, role === 'DRIVER' ? userId : undefined);
}
@Get(':id')
@ApiOperation({ summary: 'Get trip by ID (org-scoped)' })
async findOne(
@CurrentUser('organizationId') orgId: string,
@Param('id') id: string,
) {
return this.tripsService.findOne(orgId, id);
}
@Patch(':id')
// Drivers also need PATCH access so they can flip the trip status
// when they tap "Start Driving" on the mobile app. The service
// enforces that a driver can only update their own trip and may only
// change the `status` field — never reassign vehicles or dates.
@Roles(...TRIP_WRITERS, 'DRIVER')
@ApiOperation({ summary: 'Update a trip (org-scoped, drivers can change own status)' })
async update(
@CurrentUser('organizationId') orgId: string,
@CurrentUser('id') userId: string,
@CurrentUser('role') role: string,
@Param('id') id: string,
@Body() dto: UpdateTripDto,
) {
return this.tripsService.update(orgId, id, dto, { userId, role });
}
@Post(':id/dispatch')
@Roles('SUPER_ADMIN', 'ADMIN', 'OPERATIONS_MANAGER', 'DISPATCHER', 'DRIVER')
@ApiOperation({ summary: 'Dispatch a trip' })
async dispatch(
@CurrentUser('organizationId') orgId: string,
@Param('id') id: string,
) {
return this.tripsService.dispatch(orgId, id);
}
@Post(':id/complete')
@Roles('SUPER_ADMIN', 'ADMIN', 'OPERATIONS_MANAGER', 'DISPATCHER', 'DRIVER')
@ApiOperation({ summary: 'Complete a trip' })
async complete(
@CurrentUser('organizationId') orgId: string,
@Param('id') id: string,
) {
return this.tripsService.complete(orgId, id);
}
@Post(':id/cancel')
@Roles('SUPER_ADMIN', 'ADMIN', 'OPERATIONS_MANAGER', 'DISPATCHER')
@ApiOperation({ summary: 'Cancel a trip and release vehicle/bay' })
async cancel(
@CurrentUser('organizationId') orgId: string,
@Param('id') id: string,
) {
return this.tripsService.cancel(orgId, id);
}
@Delete(':id')
@Roles('SUPER_ADMIN', 'ADMIN', 'OPERATIONS_MANAGER')
@ApiOperation({ summary: 'Delete a trip' })
async remove(
@CurrentUser('organizationId') orgId: string,
@Param('id') id: string,
) {
return this.tripsService.remove(orgId, id);
}
@Post('auto-suggest')
@Roles('SUPER_ADMIN', 'ADMIN', 'OPERATIONS_MANAGER', 'PLANNER', 'DISPATCHER')
@ApiOperation({ summary: 'Generate auto-assignment suggestions for unassigned orders' })
async autoSuggest() {
return this.autoAssignService.generateSuggestions();
}
@Post('optimize-route/:tripId')
@Roles('SUPER_ADMIN', 'ADMIN', 'OPERATIONS_MANAGER', 'PLANNER', 'DISPATCHER')
@ApiOperation({ summary: 'Optimize stop sequence for a trip' })
async optimizeRoute(
@CurrentUser('organizationId') orgId: string,
@Param('tripId') tripId: string,
) {
const trip = await this.tripsService.findOne(orgId, tripId);
if (!trip) {
throw new NotFoundException(`Trip ${tripId} not found`);
}
// Build stops list from trip stops that have coordinates
const stops = trip.stops
.filter((s) => s.lat != null && s.lng != null)
.map((s) => ({
id: s.id,
lat: s.lat!,
lng: s.lng!,
type: s.stopType,
address: s.address || '',
}));
if (stops.length === 0) {
return { stops: [], totalDistanceKm: 0, estimatedDurationMin: 0 };
}
// Use vehicle's current position as start if available
const startLat = trip.vehicle?.currentLat ?? undefined;
const startLng = trip.vehicle?.currentLng ?? undefined;
return this.routeOptimizer.optimizeRoute(stops, startLat, startLng);
}
}