This commit is contained in:
natib21
2026-07-02 14:27:09 +00:00
parent 7d85f5dc2e
commit e5fed081fc
6 changed files with 36 additions and 2 deletions

View File

@@ -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,

View File

@@ -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,

View File

@@ -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<void> {
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<FirstMileStatus>('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);
}
}

View File

@@ -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");

View File

@@ -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);
},

View File

@@ -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.`,