fix schule issue and contianer type issue

This commit is contained in:
Marshal
2026-07-17 09:16:35 +00:00
parent 5a1e0dba4d
commit 7b7e7c3f62
39 changed files with 508 additions and 187 deletions

View File

@@ -12,6 +12,8 @@
import {
BadRequestException,
ConflictException,
forwardRef,
Inject,
Injectable,
Logger,
NotFoundException,
@@ -95,6 +97,7 @@ import { MaintenanceRescheduleDto } from './dto/maintenance-reschedule.dto';
import { type BookingWindowConfig } from './booking-window.config';
import { BookingWindowGateway } from './booking-window.gateway';
import { BookingNotifierService } from './booking-notifier.service';
import { BookingBatchService } from './booking-batch.service';
import {
computeFleetAvailability,
summarizeFleetWarnings,
@@ -317,6 +320,11 @@ export class TrainSchedulingService {
private readonly bookingNotifier: BookingNotifierService,
@Optional() private readonly milestoneService?: ClearanceMilestoneService,
private readonly configService?: ConfigService,
// forwardRef: BookingBatchService injects this service back; @Optional so
// existing specs that construct the service without it keep working.
@Optional()
@Inject(forwardRef(() => BookingBatchService))
private readonly bookingBatchService?: BookingBatchService,
) {}
/**
@@ -5054,6 +5062,11 @@ export class TrainSchedulingService {
wagons.reduce((sum, w) => sum + Number(w.wagonType?.lengthMeters ?? 0), 0),
);
// Wagon-slot picture for the dialog: the consist IS the schedule's booking
// capacity, so trimming/coupling wagons moves the FULL line live.
const wagonUsage =
(await this.bookingBatchService?.scheduleWagonUsage(scheduleId)) ?? null;
const mapWagon = (wagon: Wagon) => ({
id: wagon.id,
wagonNumber: wagon.wagonNumber,
@@ -5092,6 +5105,12 @@ export class TrainSchedulingService {
grossTons: roundTons(cargoTons + consistTareTons),
consistLengthMeters,
},
scheduleCapacity: wagonUsage
? {
...wagonUsage,
bookingWindowStatus: schedule.bookingWindowStatus ?? null,
}
: null,
wagons: wagons.map((wagon) => ({
...mapWagon(wagon),
loaded: loadedWagonIds.has(wagon.id),
@@ -5284,7 +5303,37 @@ export class TrainSchedulingService {
);
});
return this.getScheduleConsist(scheduleId);
// The consist IS the schedule's booking capacity, so an edit moves the
// FULL line: freeing slots on a FULL schedule reopens its window, taking
// the last slot closes it. Staff may shrink below what is already
// committed — allowed, but reported back as a warning (never silently).
const warnings: string[] = [];
const wasFull = schedule.bookingWindowStatus === 'FULL';
const usage = await this.bookingBatchService?.scheduleWagonUsage(scheduleId);
if (usage) {
const nowFull = usage.remainingSlots <= 0;
if (usage.overAllocatedBy > 0) {
warnings.push(
`The consist now has ${usage.maxWagons} wagon slot(s) but bookings already hold ` +
`${usage.allocatedWagons}${usage.overAllocatedBy} wagon(s) over capacity. ` +
'Couple more wagons or free bookings before departure.',
);
}
if (wasFull && !nowFull) {
await this.bookingBatchService?.refreshWindowStatus(scheduleId);
warnings.push(
`This schedule was FULL — the consist change freed ${usage.remainingSlots} wagon slot(s), ` +
'so it is no longer FULL and can take bookings again.',
);
} else if (!wasFull && nowFull) {
await this.bookingBatchService?.setWindow(scheduleId, 'FULL');
warnings.push(
'Every wagon slot is now taken — the schedule is FULL and stops accepting bookings.',
);
}
}
return { ...(await this.getScheduleConsist(scheduleId)), warnings };
}
/**