import { Injectable, NotFoundException, Logger } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { CreateVehicleDto, UpdateVehicleDto } from './dto'; import { executeHardDelete } from '../common/utils'; @Injectable() export class VehiclesService { private readonly logger = new Logger(VehiclesService.name); private readonly vehicleInclude = { currentDriver: true, events: { where: { deletedAt: null }, include: { driver: true, vehicle: true }, orderBy: { startTime: 'asc' as const }, }, } as const; constructor(private prisma: PrismaService) {} async create(createVehicleDto: CreateVehicleDto) { this.logger.log(`Creating vehicle: ${createVehicleDto.name}`); return this.prisma.vehicle.create({ data: createVehicleDto, include: this.vehicleInclude, }); } async findAll() { return this.prisma.vehicle.findMany({ where: { deletedAt: null }, include: this.vehicleInclude, orderBy: { name: 'asc' }, }); } async findAvailable() { return this.prisma.vehicle.findMany({ where: { deletedAt: null, status: 'AVAILABLE', }, include: { currentDriver: true, }, orderBy: { name: 'asc' }, }); } async findOne(id: string) { const vehicle = await this.prisma.vehicle.findFirst({ where: { id, deletedAt: null }, include: this.vehicleInclude, }); if (!vehicle) { throw new NotFoundException(`Vehicle with ID ${id} not found`); } return vehicle; } async update(id: string, updateVehicleDto: UpdateVehicleDto) { const vehicle = await this.findOne(id); this.logger.log(`Updating vehicle ${id}: ${vehicle.name}`); return this.prisma.vehicle.update({ where: { id: vehicle.id }, data: updateVehicleDto, include: this.vehicleInclude, }); } async remove(id: string, hardDelete = false, userRole?: string) { return executeHardDelete({ id, hardDelete, userRole, findOne: (id) => this.findOne(id), performHardDelete: (id) => this.prisma.vehicle.delete({ where: { id } }), performSoftDelete: (id) => this.prisma.vehicle.update({ where: { id }, data: { deletedAt: new Date() }, }), entityName: 'Vehicle', logger: this.logger, }); } /** * Get vehicle utilization statistics */ async getUtilization() { const now = new Date(); // Fetch vehicles with only upcoming events (filtered at database level) const vehicles = await this.prisma.vehicle.findMany({ where: { deletedAt: null }, include: { currentDriver: true, events: { where: { deletedAt: null, startTime: { gt: now }, // Only fetch upcoming events }, include: { driver: true, vehicle: true }, orderBy: { startTime: 'asc' }, }, }, orderBy: { name: 'asc' }, }); const stats = vehicles.map((vehicle) => ({ id: vehicle.id, name: vehicle.name, type: vehicle.type, seatCapacity: vehicle.seatCapacity, status: vehicle.status, upcomingTrips: vehicle.events.length, // Already filtered at DB level currentDriver: vehicle.currentDriver?.name, })); return { totalVehicles: vehicles.length, available: vehicles.filter((v) => v.status === 'AVAILABLE').length, inUse: vehicles.filter((v) => v.status === 'IN_USE').length, maintenance: vehicles.filter((v) => v.status === 'MAINTENANCE').length, reserved: vehicles.filter((v) => v.status === 'RESERVED').length, vehicles: stats, }; } }