mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 12:18:11 +00:00
Merge pull request #826 from Tria-plc/freight_feature/usermanagement
changes
This commit is contained in:
@@ -104,7 +104,8 @@ export class IntercityService {
|
||||
|
||||
async listCandidates(scheduleId: string) {
|
||||
const schedule = await this.getSchedule(scheduleId);
|
||||
const milestoneSeq = await this.routeMilestoneSequence(schedule);
|
||||
const milestones = await this.routeMilestones(schedule);
|
||||
const milestoneSeq = this.milestoneSequenceOf(schedule, milestones);
|
||||
const capacity = await this.bookingBatchService.intercityCapacity(scheduleId);
|
||||
|
||||
const waiting = milestoneSeq
|
||||
@@ -112,16 +113,32 @@ export class IntercityService {
|
||||
: [];
|
||||
const accepted = await this.findAcceptedIntercityBookings(scheduleId);
|
||||
|
||||
// Mid-corridor intercity matching needs a real stop list (>= 2 route
|
||||
// milestones). Without one the fallback is a 2-stop origin->destination
|
||||
// pseudo-route that only matches bookings on the train's exact corridor —
|
||||
// surface that so an empty candidate list isn't misread as "nobody waiting".
|
||||
const warning =
|
||||
milestoneSeq == null
|
||||
? 'This schedule has no route or origin/destination set, so no intercity corridors can be served.'
|
||||
: schedule.routeId && milestones.length < 2
|
||||
? "This schedule's route has no stop list (needs at least 2 route milestones), so mid-corridor intercity bookings cannot be matched — only bookings on the train's exact origin→destination will appear."
|
||||
: null;
|
||||
|
||||
return {
|
||||
scheduleId,
|
||||
routeId: schedule.routeId ?? null,
|
||||
warning,
|
||||
// Segment-based: "remaining" is the most-open edge; each candidate's
|
||||
// `fits` is judged against ITS OWN leg, so a booking on a free leg fits
|
||||
// even when the train is full elsewhere.
|
||||
remaining: capacity?.budget.maxRemaining() ?? null,
|
||||
candidates: waiting.map((booking) => {
|
||||
const need = capacity?.needFor(booking) ?? null;
|
||||
const leg = capacity?.budget.legOf(
|
||||
// legForYards, not legOf: on a built train the budget is a single
|
||||
// whole-route edge (see intercityCapacity), so a mid-corridor booking
|
||||
// must draw from that one pool via the whole-route fallback. On a
|
||||
// locomotive-derived schedule it still resolves to the booking's own leg.
|
||||
const leg = capacity?.budget.legForYards(
|
||||
booking.originYardId,
|
||||
booking.destinationYardId,
|
||||
);
|
||||
@@ -186,14 +203,17 @@ export class IntercityService {
|
||||
continue;
|
||||
}
|
||||
const need = capacity.needFor(booking);
|
||||
const leg = budget.legOf(booking.originYardId, booking.destinationYardId);
|
||||
// Segment-based: only the booking's own leg must have room, so an
|
||||
// intercity booking still boards a train that is full on other legs.
|
||||
if (!leg || !budget.fits(need, leg)) {
|
||||
// legForYards, not legOf: a built train's budget is a single whole-route
|
||||
// pool (mid-corridor wagons are committed for the whole trip and never
|
||||
// reloaded), so the booking draws from that pool via the whole-route
|
||||
// fallback; a locomotive-derived schedule still gets the booking's own
|
||||
// leg, so it can still board a train that is full only on other legs.
|
||||
const leg = budget.legForYards(booking.originYardId, booking.destinationYardId);
|
||||
if (!budget.fits(need, leg)) {
|
||||
rejected.push({
|
||||
bookingId,
|
||||
reason:
|
||||
'Does not fit the remaining wagon/weight/length capacity on its leg',
|
||||
'Does not fit the remaining wagon/weight/length capacity for this train',
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@@ -244,16 +264,21 @@ export class IntercityService {
|
||||
* so an intercity booking exactly matching the train's own corridor still
|
||||
* qualifies.
|
||||
*/
|
||||
private async routeMilestoneSequence(
|
||||
private async routeMilestones(
|
||||
schedule: TrainSchedule,
|
||||
): Promise<Map<string, number> | null> {
|
||||
if (schedule.routeId) {
|
||||
const milestones = await this.dataSource
|
||||
.getRepository(RouteMilestone)
|
||||
.find({ where: { routeId: schedule.routeId }, order: { sequenceNo: 'ASC' } });
|
||||
if (milestones.length >= 2) {
|
||||
return new Map(milestones.map((m) => [m.yardId, m.sequenceNo]));
|
||||
}
|
||||
): Promise<RouteMilestone[]> {
|
||||
if (!schedule.routeId) return [];
|
||||
return this.dataSource
|
||||
.getRepository(RouteMilestone)
|
||||
.find({ where: { routeId: schedule.routeId }, order: { sequenceNo: 'ASC' } });
|
||||
}
|
||||
|
||||
private milestoneSequenceOf(
|
||||
schedule: TrainSchedule,
|
||||
milestones: RouteMilestone[],
|
||||
): Map<string, number> | null {
|
||||
if (milestones.length >= 2) {
|
||||
return new Map(milestones.map((m) => [m.yardId, m.sequenceNo]));
|
||||
}
|
||||
if (schedule.originStationId && schedule.destinationStationId) {
|
||||
return new Map([
|
||||
@@ -264,6 +289,15 @@ export class IntercityService {
|
||||
return null;
|
||||
}
|
||||
|
||||
private async routeMilestoneSequence(
|
||||
schedule: TrainSchedule,
|
||||
): Promise<Map<string, number> | null> {
|
||||
return this.milestoneSequenceOf(
|
||||
schedule,
|
||||
await this.routeMilestones(schedule),
|
||||
);
|
||||
}
|
||||
|
||||
/** Waiting = ready intercity bookings not yet on any train, corridor on this route. */
|
||||
private async findWaitingIntercityBookings(
|
||||
milestoneSeq: Map<string, number>,
|
||||
|
||||
Reference in New Issue
Block a user