From e5fed081fccd45f26fa62c2edbd34f6c95062331 Mon Sep 17 00:00:00 2001 From: natib21 Date: Thu, 2 Jul 2026 14:27:09 +0000 Subject: [PATCH] fix --- .../src/modules/bookings/bookings.module.ts | 2 ++ .../src/modules/bookings/bookings.service.ts | 23 +++++++++++++++++++ .../src/modules/vehicles/vehicles.service.ts | 6 +++-- .../src/pages/bookings/BookingDetailPage.tsx | 1 + .../src/pages/operations/FirstMilePage.tsx | 3 +++ .../src/pages/operations/LastMilePage.tsx | 3 +++ 6 files changed, 36 insertions(+), 2 deletions(-) diff --git a/apps/edr-freight-api/src/modules/bookings/bookings.module.ts b/apps/edr-freight-api/src/modules/bookings/bookings.module.ts index 4cd5d10df..72dbab183 100644 --- a/apps/edr-freight-api/src/modules/bookings/bookings.module.ts +++ b/apps/edr-freight-api/src/modules/bookings/bookings.module.ts @@ -39,6 +39,7 @@ import { ContractRendererService } from "../../contracts/contract-renderer.servi import { ContractTemplateResolver } from "../../contracts/contract-template.resolver"; import { ContractViewModelBuilder } from "../../contracts/contract-view-model.builder"; import { TrainSchedulingModule } from "../train-scheduling/train-scheduling.module"; +import { VehiclesModule } from "../vehicles/vehicles.module"; @Module({ imports: [ @@ -58,6 +59,7 @@ import { TrainSchedulingModule } from "../train-scheduling/train-scheduling.modu forwardRef(() => TrainSchedulingModule), FilesModule, MinioModule, + VehiclesModule, CompaniesModule, // CustomersModule, RuleEngineModule, diff --git a/apps/edr-freight-api/src/modules/bookings/bookings.service.ts b/apps/edr-freight-api/src/modules/bookings/bookings.service.ts index a4636cbf7..99d69c25f 100644 --- a/apps/edr-freight-api/src/modules/bookings/bookings.service.ts +++ b/apps/edr-freight-api/src/modules/bookings/bookings.service.ts @@ -31,6 +31,8 @@ import { ServiceType } from '../rule-engine/entities/service-type.entity'; import { TrainSchedule } from '../train-schedules/entities/train-schedule.entity'; import { BookingsRepository } from './bookings.repository'; import { ConsolidationService } from './consolidation.service'; +import { VehiclesService } from '../vehicles/vehicles.service'; +import { VehicleAvailability } from '../vehicles/entities/vehicle.entity'; import { assertFreightShape } from './booking-freight.util'; import { CreateBookingContainerDto, CreateBookingDto } from './dto/create-booking.dto'; import { mapStatusCountsToTabs } from './booking-list-tabs.config'; @@ -81,6 +83,7 @@ export class BookingsService { private readonly ruleEngineService: RuleEngineService, private readonly containerTypesService: ContainerTypesService, private readonly consolidationService: ConsolidationService, + private readonly vehiclesService: VehiclesService, ) {} /** Resolve trade direction from yard countries; reject client mismatch. */ @@ -1348,6 +1351,16 @@ export class BookingsService { throw new NotFoundException(`Booking ${bookingId} not found`); } + const previousAllocations = await this.dataSource.manager.find(BookingContainerAllocation, { + where: { + bookingId, + containerId: In(allocations.map((a) => a.containerId)), + }, + }); + const previousVehicleIds = previousAllocations + .map((a) => a.vehicleId) + .filter((id): id is string => Boolean(id)); + await this.dataSource.transaction(async (manager) => { for (const allocation of allocations) { await manager.delete(BookingContainerAllocation, { @@ -1364,6 +1377,16 @@ export class BookingsService { } }); + const vehicleIds = new Set(allocations.map((a) => a.vehicleId)); + await Promise.all( + [...vehicleIds].map((vehicleId) => + this.vehiclesService.setAvailability(vehicleId, VehicleAvailability.BUSY), + ), + ); + await this.vehiclesService.releaseIfUnused( + previousVehicleIds.filter((id) => !vehicleIds.has(id)), + ); + return { success: true, allocated: allocations.length, 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 a27969e2b..5da3b448d 100644 --- a/apps/edr-freight-api/src/modules/vehicles/vehicles.service.ts +++ b/apps/edr-freight-api/src/modules/vehicles/vehicles.service.ts @@ -8,6 +8,7 @@ import { FirstMile, FirstMileStatus } from '../first-mile/entities/first-mile.en import { FirstMileContainerAllocation } from '../first-mile/entities/first-mile-container-allocation.entity'; import { LastMile, LastMileStatus } from '../last-mile/entities/last-mile.entity'; import { LastMileContainerAllocation } from '../last-mile/entities/last-mile-container-allocation.entity'; +import { BookingContainerAllocation } from '../bookings/entities/booking-container-allocation.entity'; @Injectable() export class VehiclesService { @@ -113,7 +114,7 @@ export class VehiclesService { async releaseIfUnused(vehicleIds: string[]): Promise { const manager = this.vehicleRepo.manager; for (const vehicleId of [...new Set(vehicleIds)]) { - const [fmRecords, lmRecords, fmAllocations, lmAllocations] = await Promise.all([ + const [fmRecords, lmRecords, fmAllocations, lmAllocations, bookingAllocations] = await Promise.all([ manager.count(FirstMile, { where: { vehicleId, status: Not('RECEIVED_TO_PORT') }, }), @@ -134,8 +135,9 @@ export class VehiclesService { .andWhere('lm.status != :done', { done: 'DELIVERED' }) .andWhere('lm.deletedAt IS NULL') .getCount(), + manager.count(BookingContainerAllocation, { where: { vehicleId } }), ]); - if (fmRecords + lmRecords + fmAllocations + lmAllocations === 0) { + if (fmRecords + lmRecords + fmAllocations + lmAllocations + bookingAllocations === 0) { await this.setAvailability(vehicleId, VehicleAvailability.FREE); } } diff --git a/apps/edr-freight-web/backoffice/src/pages/bookings/BookingDetailPage.tsx b/apps/edr-freight-web/backoffice/src/pages/bookings/BookingDetailPage.tsx index bf01c4818..d49e24f95 100644 --- a/apps/edr-freight-web/backoffice/src/pages/bookings/BookingDetailPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/bookings/BookingDetailPage.tsx @@ -33,6 +33,7 @@ const BookingDetailPage = () => { onSuccess: () => { toast.success("Containers allocated"); qc.invalidateQueries({ queryKey: QUERY_KEYS.BOOKINGS.byId(id ?? "") }); + qc.invalidateQueries({ queryKey: ["vehicles"] }); }, onError: () => { toast.error("Failed to allocate containers"); diff --git a/apps/edr-freight-web/backoffice/src/pages/operations/FirstMilePage.tsx b/apps/edr-freight-web/backoffice/src/pages/operations/FirstMilePage.tsx index 4f401b3bc..78b15525b 100644 --- a/apps/edr-freight-web/backoffice/src/pages/operations/FirstMilePage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/operations/FirstMilePage.tsx @@ -400,6 +400,7 @@ const FirstMilePage = () => { firstMileService.update(id, data), onSuccess: () => { void qc.invalidateQueries({ queryKey: QUERY_KEYS.FIRST_MILE.list() }); + void qc.invalidateQueries({ queryKey: ["vehicles"] }); }, onError: () => { toast({ title: "Update failed", variant: "destructive" }); @@ -444,6 +445,7 @@ const FirstMilePage = () => { }, onSuccess: () => { void qc.invalidateQueries({ queryKey: QUERY_KEYS.FIRST_MILE.ROOT }); + void qc.invalidateQueries({ queryKey: ["vehicles"] }); toast({ title: "Booking accepted", description: "First-mile leg created successfully." }); closeAccept(); }, @@ -460,6 +462,7 @@ const FirstMilePage = () => { onSuccess: () => { toast({ title: "Containers allocated" }); void qc.invalidateQueries({ queryKey: QUERY_KEYS.FIRST_MILE.byId(containerAllocationFirstMileId ?? "") }); + void qc.invalidateQueries({ queryKey: ["vehicles"] }); setContainerAllocationOpen(false); setContainerAllocationFirstMileId(null); }, diff --git a/apps/edr-freight-web/backoffice/src/pages/operations/LastMilePage.tsx b/apps/edr-freight-web/backoffice/src/pages/operations/LastMilePage.tsx index ae03aab75..9284c4dae 100644 --- a/apps/edr-freight-web/backoffice/src/pages/operations/LastMilePage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/operations/LastMilePage.tsx @@ -377,6 +377,7 @@ const LastMilePage = () => { lastMileService.update(id, data), onSuccess: () => { void qc.invalidateQueries({ queryKey: QUERY_KEYS.LAST_MILE.ROOT }); + void qc.invalidateQueries({ queryKey: ["vehicles"] }); }, onError: () => { toast({ title: "Update failed", variant: "destructive" }); @@ -415,6 +416,7 @@ const LastMilePage = () => { onSuccess: () => { toast({ title: "Containers allocated", variant: "default" }); void qc.invalidateQueries({ queryKey: QUERY_KEYS.LAST_MILE.byId(activeId ?? "") }); + void qc.invalidateQueries({ queryKey: ["vehicles"] }); closeAllocation(); }, onError: () => { @@ -453,6 +455,7 @@ const LastMilePage = () => { }, onSuccess: (created) => { void qc.invalidateQueries({ queryKey: QUERY_KEYS.LAST_MILE.ROOT }); + void qc.invalidateQueries({ queryKey: ["vehicles"] }); toast({ title: "Last-mile leg created", description: `${created.length} ${created.length === 1 ? "delivery" : "deliveries"} accepted successfully.`,