enhance train scheduling and booking management

This commit is contained in:
Marshal
2026-07-07 21:38:45 +00:00
parent 8addfdf3e2
commit 87a95b37bf
13 changed files with 656 additions and 28 deletions

View File

@@ -80,6 +80,10 @@ export interface BatchBoardBooking {
lengthMeters: number;
paymentDeadline: string | null;
state: BatchBoardBookingState;
/** Rule-engine priority score used to rank the batch (higher = boards first). */
priorityScore: number;
/** CONTAINER | BULK — for the priority-tracking visuals. */
freightType: string | null;
}
export type BookingAllocationStatus =
@@ -638,6 +642,8 @@ export class BookingBatchService implements OnModuleInit {
? b.paymentDeadline.toISOString()
: null,
state: this.boardState(b, linkedIds.has(b.id)),
priorityScore: Number(b.priorityScore ?? 0),
freightType: b.freightType ?? null,
};
});
@@ -726,6 +732,8 @@ export class BookingBatchService implements OnModuleInit {
? b.paymentDeadline.toISOString()
: null,
state: this.boardState(b, linkedIds.has(b.id)),
priorityScore: Number(b.priorityScore ?? 0),
freightType: b.freightType ?? null,
fullyExecutedAt: b.fullyExecutedAt
? b.fullyExecutedAt.toISOString()
: null,
@@ -1026,8 +1034,9 @@ export class BookingBatchService implements OnModuleInit {
const units = this.groupConsolidatedPool(pool);
let armed = false;
// TODO(change-c-diagnostics): remove once the "only 1 reserved" capacity cause
// is confirmed. Logs the caps + pool so we can see which axis rejects unit #2.
// Batch fill trace: caps + pool at entry. Kept on debug level — invaluable when
// reservations trickle instead of landing in one pass (a reserve() throwing
// mid-loop, e.g. schema drift, or a mis-synced capacity cap).
this.logger.debug(
`[fillSchedule ${scheduleId}] limits=${JSON.stringify(limits)} ` +
`maxWagons=${schedule.maxWagons} remaining=${JSON.stringify(budget.maxRemaining())} ` +
@@ -1045,7 +1054,7 @@ export class BookingBatchService implements OnModuleInit {
// stands for the pair.
const leg = budget.legForYards(booking.originYardId, booking.destinationYardId);
// TODO(change-c-diagnostics): remove once the capacity cause is confirmed.
// Per-unit fit trace: which axis (wagons/weight/length) admits or rejects.
this.logger.debug(
`[fillSchedule ${scheduleId}] unit ${booking.reference}: need=${JSON.stringify(need)} ` +
`roomOnLeg=${JSON.stringify(budget.remainingFor(leg))} fits=${budget.fits(need, leg)}`,
@@ -1175,8 +1184,7 @@ export class BookingBatchService implements OnModuleInit {
// consolidated booking whose partner isn't ready this cycle is skipped.
const units = this.groupConsolidatedPool(pool);
// TODO(change-c-diagnostics): remove once the "only 1 reserved" capacity cause
// is confirmed. Shows each train's caps + the day pool size.
// Batch fill trace: each train's caps + the day pool size at entry.
this.logger.debug(
`[fillRouteDay ${originYardId}->${destinationYardId} ${day}] ` +
`trains=${trains.map((t) => `${t.id}:${JSON.stringify(t.budget.maxRemaining())}`).join(",")} ` +
@@ -1201,7 +1209,7 @@ export class BookingBatchService implements OnModuleInit {
return leg != null && t.budget.fits(need, leg);
});
// TODO(change-c-diagnostics): remove once the capacity cause is confirmed.
// Per-unit trace: chosen train + each train's remaining room on this leg.
this.logger.debug(
`[fillRouteDay] unit ${booking.reference}: need=${JSON.stringify(need)} ` +
`targetTrain=${target?.id ?? "none"} ` +