From 89d82f45c65b75b40f17dc821643d7df2b5862f5 Mon Sep 17 00:00:00 2001 From: Hagernesh Date: Tue, 21 Jul 2026 14:00:38 +0000 Subject: [PATCH] fix padding on portal --- .../vehicles/vehicles.driver-guard.spec.ts | 44 +++++++++++++++++++ .../src/modules/vehicles/vehicles.service.ts | 27 ++++++++++++ .../components/WarehouseLocationCard.tsx | 2 +- 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 apps/edr-freight-api/src/modules/vehicles/vehicles.driver-guard.spec.ts diff --git a/apps/edr-freight-api/src/modules/vehicles/vehicles.driver-guard.spec.ts b/apps/edr-freight-api/src/modules/vehicles/vehicles.driver-guard.spec.ts new file mode 100644 index 000000000..cb810abaf --- /dev/null +++ b/apps/edr-freight-api/src/modules/vehicles/vehicles.driver-guard.spec.ts @@ -0,0 +1,44 @@ +import { ConflictException } from '@nestjs/common'; + +import { VehiclesService } from './vehicles.service'; + +// One driver ⇒ one truck: create/update must refuse a driver already assigned +// to another (non-deleted) vehicle until they are detached. +describe('VehiclesService driver assignment guard', () => { + const otherTruck = { id: 'v2', plateNumber: '3-11111', assignedDriverId: 'd1' }; + + const makeService = (findOne: jest.Mock) => + new VehiclesService( + { findOne, create: jest.fn((x) => x), save: jest.fn(async (x) => x) } as any, + { record: jest.fn() } as any, + ); + + it('rejects create when the driver is on another truck', async () => { + // First findOne = plate uniqueness (null), second = driver holder. + const findOne = jest.fn().mockResolvedValueOnce(null).mockResolvedValueOnce(otherTruck); + const svc = makeService(findOne); + await expect( + svc.create({ plateNumber: '3-22222', vehicleType: 'TRUCK', assignedDriverId: 'd1' } as any), + ).rejects.toThrow(ConflictException); + }); + + it('rejects update when reassigning a driver still attached elsewhere', async () => { + const findOne = jest + .fn() + .mockResolvedValueOnce({ id: 'v1', plateNumber: '3-22222', assignedDriverId: null }) // findById + .mockResolvedValueOnce(otherTruck); // driver holder + const svc = makeService(findOne); + await expect(svc.update('v1', { assignedDriverId: 'd1' } as any)).rejects.toThrow( + ConflictException, + ); + }); + + it('allows update that keeps the same driver on the same truck', async () => { + const findOne = jest + .fn() + .mockResolvedValueOnce({ id: 'v1', plateNumber: '3-22222', assignedDriverId: 'd1' }); + const svc = makeService(findOne); + await expect(svc.update('v1', { assignedDriverId: 'd1' } as any)).resolves.toBeDefined(); + expect(findOne).toHaveBeenCalledTimes(1); // guard skipped — no holder lookup + }); +}); diff --git a/apps/edr-freight-api/src/modules/vehicles/vehicles.service.ts b/apps/edr-freight-api/src/modules/vehicles/vehicles.service.ts index 25260e86f..98d38cbce 100644 --- a/apps/edr-freight-api/src/modules/vehicles/vehicles.service.ts +++ b/apps/edr-freight-api/src/modules/vehicles/vehicles.service.ts @@ -20,6 +20,25 @@ export class VehiclesService { private readonly history: FleetHistoryService, ) {} + /** + * A driver holds one truck at a time — reassignment requires detaching them + * from their current truck first. + * ponytail: app-level guard only (race window); add a partial unique index on + * assigned_driver_id if concurrent fleet edits ever become real. + */ + private async assertDriverUnassigned(driverId: string, exceptVehicleId?: string): Promise { + const holder = await this.vehicleRepo.findOne({ + where: exceptVehicleId + ? { assignedDriverId: driverId, id: Not(exceptVehicleId) } + : { assignedDriverId: driverId }, + }); + if (holder) { + throw new ConflictException( + `This driver is already assigned to truck ${holder.plateNumber ?? holder.code ?? holder.id} — detach the driver from that truck first`, + ); + } + } + async create(dto: CreateVehicleDto): Promise { const existing = await this.vehicleRepo.findOne({ where: { plateNumber: dto.plateNumber }, @@ -31,6 +50,10 @@ export class VehiclesService { ); } + if (dto.assignedDriverId) { + await this.assertDriverUnassigned(dto.assignedDriverId); + } + const registrationNumber = `REG-${dto.vehicleType}-${Date.now()}`; const vehicle = this.vehicleRepo.create({ ...dto, @@ -121,6 +144,10 @@ export class VehiclesService { } } + if (dto.assignedDriverId && dto.assignedDriverId !== vehicle.assignedDriverId) { + await this.assertDriverUnassigned(dto.assignedDriverId, id); + } + const prev = { assignedDriverId: vehicle.assignedDriverId, assignedDriverName: vehicle.assignedDriverName, diff --git a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/WarehouseLocationCard.tsx b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/WarehouseLocationCard.tsx index 64320a968..cf20912cd 100644 --- a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/WarehouseLocationCard.tsx +++ b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/WarehouseLocationCard.tsx @@ -31,7 +31,7 @@ export function WarehouseLocationCard({ bookingId }: WarehouseLocationCardProps) const latest = items[0]; return ( - +