mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 18:48:11 +00:00
fix(train-scheduling): move loose wagons and locomotives on train merge
This commit is contained in:
@@ -68,6 +68,23 @@ describe('computeScheduleWagonUsage', () => {
|
||||
expect(usage.wagonsRemaining).toBe(0);
|
||||
});
|
||||
|
||||
it('sells against the planned ceiling, not the partially coupled consist', () => {
|
||||
// S-2026-00003: planned 3 wagons, 2 coupled + allocated for a paid booking
|
||||
// that reserved 2 — the list read "2/2 used, 0 bookable" while the detail
|
||||
// page and the booking gate (remainingWagonsForLeg vs maxWagons) both said
|
||||
// 1 wagon was still free. Wagons couple on demand; the ceiling is capacity.
|
||||
const usage = computeScheduleWagonUsage({
|
||||
wagonSlots: Array(2).fill(slot(true)),
|
||||
storedWagonCount: 2,
|
||||
scheduleBookings: [booking(2)],
|
||||
maxWagons: 3,
|
||||
});
|
||||
|
||||
expect(usage.wagonsUsed).toBe(2);
|
||||
expect(usage.wagonsTotal).toBe(3);
|
||||
expect(usage.wagonsRemaining).toBe(1);
|
||||
});
|
||||
|
||||
it('falls back to the stored counter when slot rows were not loaded', () => {
|
||||
const usage = computeScheduleWagonUsage({
|
||||
wagonSlots: [],
|
||||
|
||||
@@ -20,11 +20,11 @@ export interface ScheduleBookingLike {
|
||||
export interface ScheduleWagonUsage {
|
||||
/** Coupled slots carrying at least one booking allocation. */
|
||||
wagonsUsed: number;
|
||||
/** Coupled consist size — the denominator of `wagonsUsed`. */
|
||||
/** Schedule capacity — the larger of coupled consist and planned ceiling. */
|
||||
wagonsTotal: number;
|
||||
/** Wagons claimed by bookings, including bookings that have not paid. */
|
||||
wagonsReserved: number;
|
||||
/** Consist minus what bookings have claimed — what is still bookable. */
|
||||
/** Capacity minus what bookings have claimed — what is still bookable. */
|
||||
wagonsRemaining: number;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ export function computeScheduleWagonUsage(input: {
|
||||
/** Stored counter; used only when the slot rows were not loaded. */
|
||||
storedWagonCount?: number | null;
|
||||
scheduleBookings?: ScheduleBookingLike[] | null;
|
||||
/** Planned wagon ceiling (`maxWagons`) — what booking capacity is sold against. */
|
||||
maxWagons?: number | null;
|
||||
}): ScheduleWagonUsage {
|
||||
const slots = input.wagonSlots ?? [];
|
||||
|
||||
@@ -42,7 +44,14 @@ export function computeScheduleWagonUsage(input: {
|
||||
|
||||
// Prefer live slot rows; the stored counter drifts when a consist is edited
|
||||
// without a recompute, which is why the list and detail disagreed on totals.
|
||||
const wagonsTotal = slots.length || (input.storedWagonCount ?? 0);
|
||||
const coupled = slots.length || (input.storedWagonCount ?? 0);
|
||||
|
||||
// Wagons are coupled on demand as bookings are allocated, so a partially
|
||||
// built consist does not cap what is bookable — the planned ceiling does
|
||||
// (remainingWagonsForLeg sells against maxWagons). Without this, a schedule
|
||||
// planned for 3 wagons with 2 coupled+allocated read "2/2 used, 0 bookable"
|
||||
// while its detail page and the booking gate both said 1 wagon was free.
|
||||
const wagonsTotal = Math.max(coupled, input.maxWagons ?? 0);
|
||||
|
||||
// An unpaid booking still holds its wagons, so reserved space is NOT bookable.
|
||||
const wagonsReserved = (input.scheduleBookings ?? []).reduce(
|
||||
|
||||
Reference in New Issue
Block a user