mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 17:50:54 +00:00
199 lines
6.3 KiB
TypeScript
199 lines
6.3 KiB
TypeScript
import { Injectable, NotFoundException, ConflictException, BadRequestException } from '@nestjs/common';
|
|
import { InjectRepository } from '@nestjs/typeorm';
|
|
import { Repository } from 'typeorm';
|
|
import { CreateDriverDto } from './dto/create-driver.dto';
|
|
import { UpdateDriverDto } from './dto/update-driver.dto';
|
|
import { Driver, DriverStatus } from './entities/driver.entity';
|
|
import { FleetHistoryService } from '../fleet-history/fleet-history.service';
|
|
import { FleetEventType } from '../fleet-history/entities/fleet-event.entity';
|
|
import { FilesService } from '../files/files.service';
|
|
|
|
/** Resource + code the driver-documents upload area is stored under. */
|
|
const DRIVER_DOCS_RESOURCE = 'driver';
|
|
const DRIVER_DOCS_CODE = 'driver_docs';
|
|
|
|
@Injectable()
|
|
export class DriversService {
|
|
constructor(
|
|
@InjectRepository(Driver)
|
|
private readonly driverRepo: Repository<Driver>,
|
|
private readonly history: FleetHistoryService,
|
|
private readonly filesService: FilesService,
|
|
) {}
|
|
|
|
/** Upload one or more driver documents (code "driver_docs"). */
|
|
async uploadDocuments(driverId: string, files: Express.Multer.File[]) {
|
|
const driver = await this.driverRepo.findOneBy({ id: driverId });
|
|
if (!driver) throw new NotFoundException(`Driver ${driverId} not found`);
|
|
if (!files?.length) throw new BadRequestException('No files provided');
|
|
return Promise.all(
|
|
files.map((file) =>
|
|
this.filesService.upload({
|
|
resourceId: driverId,
|
|
resource: DRIVER_DOCS_RESOURCE,
|
|
code: DRIVER_DOCS_CODE,
|
|
file,
|
|
}),
|
|
),
|
|
);
|
|
}
|
|
|
|
/** List a driver's uploaded documents (code "driver_docs"). */
|
|
async listDocuments(driverId: string) {
|
|
const all = await this.filesService.findByResource(driverId, DRIVER_DOCS_RESOURCE);
|
|
return all.filter((f) => f.code === DRIVER_DOCS_CODE);
|
|
}
|
|
|
|
/** Delete a single driver document by file id. */
|
|
async removeDocument(fileId: string): Promise<void> {
|
|
await this.filesService.remove(fileId);
|
|
}
|
|
|
|
async create(dto: CreateDriverDto): Promise<Driver> {
|
|
if (dto.faydaVerified !== true) {
|
|
throw new BadRequestException(
|
|
'Driver identity must be verified with Fayda before saving',
|
|
);
|
|
}
|
|
|
|
const existing = await this.driverRepo.findOne({
|
|
where: [
|
|
{ licenseNumber: dto.licenseNumber },
|
|
{ email: dto.email },
|
|
{ phoneNumber: dto.phoneNumber },
|
|
],
|
|
});
|
|
|
|
if (existing) {
|
|
if (existing.licenseNumber === dto.licenseNumber) {
|
|
throw new ConflictException(`Driver with license number ${dto.licenseNumber} already exists`);
|
|
}
|
|
if (existing.email === dto.email) {
|
|
throw new ConflictException(`Driver with email ${dto.email} already exists`);
|
|
}
|
|
if (existing.phoneNumber === dto.phoneNumber) {
|
|
throw new ConflictException(`Driver with phone number ${dto.phoneNumber} already exists`);
|
|
}
|
|
}
|
|
|
|
if (dto.faydaSub) {
|
|
const dupe = await this.driverRepo.findOne({
|
|
where: { faydaSub: dto.faydaSub },
|
|
});
|
|
if (dupe) {
|
|
throw new ConflictException(
|
|
'A driver is already registered for this Fayda identity',
|
|
);
|
|
}
|
|
}
|
|
|
|
const driver = this.driverRepo.create(dto);
|
|
const saved = await this.driverRepo.save(driver);
|
|
|
|
await this.history.record({
|
|
eventType: FleetEventType.DRIVER_REGISTERED,
|
|
driverId: saved.id,
|
|
label: `${saved.firstName ?? ''} ${saved.lastName ?? ''}`.trim() || null,
|
|
toValue: saved.status ?? null,
|
|
});
|
|
|
|
return saved;
|
|
}
|
|
|
|
async findAll(query: {
|
|
search?: string;
|
|
status?: DriverStatus | string;
|
|
page?: number;
|
|
limit?: number;
|
|
sortBy?: string;
|
|
sortOrder?: 'ASC' | 'DESC';
|
|
} = {}): Promise<Driver[]> {
|
|
const qb = this.driverRepo.createQueryBuilder('d');
|
|
|
|
if (query.search) {
|
|
qb.where(
|
|
'(d.firstName ILIKE :search OR d.lastName ILIKE :search OR d.email ILIKE :search OR d.licenseNumber ILIKE :search OR d.phoneNumber ILIKE :search)',
|
|
{ search: `%${query.search}%` },
|
|
);
|
|
}
|
|
|
|
if (query.status) {
|
|
qb.andWhere('d.status = :status', { status: query.status });
|
|
}
|
|
|
|
const sortBy = query.sortBy && ['firstName', 'lastName', 'status', 'createdAt'].includes(query.sortBy)
|
|
? query.sortBy
|
|
: 'createdAt';
|
|
const sortOrder = (query.sortOrder ?? 'DESC').toUpperCase();
|
|
|
|
return qb
|
|
.orderBy(`d.${sortBy}`, sortOrder as 'ASC' | 'DESC')
|
|
.getMany();
|
|
}
|
|
|
|
async findById(id: string): Promise<Driver> {
|
|
const driver = await this.driverRepo.findOne({ where: { id } });
|
|
if (!driver) {
|
|
throw new NotFoundException(`Driver ${id} not found`);
|
|
}
|
|
return driver;
|
|
}
|
|
|
|
async update(id: string, dto: UpdateDriverDto): Promise<Driver> {
|
|
const driver = await this.findById(id);
|
|
|
|
if (dto.licenseNumber && dto.licenseNumber !== driver.licenseNumber) {
|
|
const existing = await this.driverRepo.findOne({
|
|
where: { licenseNumber: dto.licenseNumber },
|
|
});
|
|
if (existing) {
|
|
throw new ConflictException(`Driver with license number ${dto.licenseNumber} already exists`);
|
|
}
|
|
}
|
|
|
|
if (dto.email && dto.email !== driver.email) {
|
|
const existing = await this.driverRepo.findOne({
|
|
where: { email: dto.email },
|
|
});
|
|
if (existing) {
|
|
throw new ConflictException(`Driver with email ${dto.email} already exists`);
|
|
}
|
|
}
|
|
|
|
if (dto.phoneNumber && dto.phoneNumber !== driver.phoneNumber) {
|
|
const existing = await this.driverRepo.findOne({
|
|
where: { phoneNumber: dto.phoneNumber },
|
|
});
|
|
if (existing) {
|
|
throw new ConflictException(`Driver with phone number ${dto.phoneNumber} already exists`);
|
|
}
|
|
}
|
|
|
|
if (dto.faydaSub && dto.faydaSub !== driver.faydaSub) {
|
|
const dupe = await this.driverRepo.findOne({
|
|
where: { faydaSub: dto.faydaSub },
|
|
});
|
|
if (dupe) {
|
|
throw new ConflictException(
|
|
'A driver is already registered for this Fayda identity',
|
|
);
|
|
}
|
|
}
|
|
|
|
Object.assign(driver, dto);
|
|
|
|
if (driver.faydaVerified !== true) {
|
|
throw new BadRequestException(
|
|
'Driver identity must be verified with Fayda before saving',
|
|
);
|
|
}
|
|
|
|
return this.driverRepo.save(driver);
|
|
}
|
|
|
|
async remove(id: string): Promise<void> {
|
|
await this.findById(id);
|
|
await this.driverRepo.softDelete(id);
|
|
}
|
|
}
|