mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
fix issue
This commit is contained in:
@@ -1516,6 +1516,7 @@ export class BookingBatchService implements OnModuleInit {
|
||||
stock,
|
||||
leg,
|
||||
this.scarcityRankForPool([primary], allowedWagonTypes),
|
||||
wagonTypeIds,
|
||||
)
|
||||
: null;
|
||||
const seated = useSmart
|
||||
@@ -2357,7 +2358,7 @@ export class BookingBatchService implements OnModuleInit {
|
||||
!perItemBulk &&
|
||||
wagonTypeIds.length > 0;
|
||||
const smart = useSmart
|
||||
? this.smartBulkNeed(booking, wagonDims, stock, leg, scarcityRank)
|
||||
? this.smartBulkNeed(booking, wagonDims, stock, leg, scarcityRank, wagonTypeIds)
|
||||
: null;
|
||||
const admitted = useSmart
|
||||
? smart != null && budget.fits(smart.need, leg)
|
||||
@@ -2653,7 +2654,14 @@ export class BookingBatchService implements OnModuleInit {
|
||||
const leg = legOn(t);
|
||||
if (leg == null) continue;
|
||||
if (useSmart) {
|
||||
const probe = this.smartBulkNeed(booking, wagonDims, t.stock, leg, scarcityRank);
|
||||
const probe = this.smartBulkNeed(
|
||||
booking,
|
||||
wagonDims,
|
||||
t.stock,
|
||||
leg,
|
||||
scarcityRank,
|
||||
wagonTypeIds,
|
||||
);
|
||||
if (probe != null && t.budget.fits(probe.need, leg)) {
|
||||
smart = probe;
|
||||
target = t;
|
||||
@@ -2858,10 +2866,14 @@ export class BookingBatchService implements OnModuleInit {
|
||||
// other 4 leave as the usual remainder booking, instead of paying for
|
||||
// 20 and stalling at allocation on wagon 17.
|
||||
if (cappedBulk && wagonDims) {
|
||||
const best = this.allowedDimsWithTypes(booking, wagonDims)
|
||||
.filter((o): o is { wagonTypeId: string; dims: PerWagonDims } =>
|
||||
o.wagonTypeId != null,
|
||||
)
|
||||
// Types resolved from the id list (join tables), never the pool
|
||||
// entity's unloaded cargoType.wagonTypes relation — see smartBulkNeed.
|
||||
const best = [...new Set(wagonTypeIds)]
|
||||
.map((wagonTypeId) => ({
|
||||
wagonTypeId,
|
||||
dims: wagonDims.byWagonTypeId.get(wagonTypeId),
|
||||
}))
|
||||
.filter((o): o is { wagonTypeId: string; dims: PerWagonDims } => o.dims != null)
|
||||
.map((o) => ({
|
||||
...o,
|
||||
free: c.stock?.availableFor([o.wagonTypeId], leg) ?? 0,
|
||||
@@ -5074,7 +5086,7 @@ export class BookingBatchService implements OnModuleInit {
|
||||
Number(b.bulkTotalWeightTons ?? 0) > 0 &&
|
||||
Number(b.cargoTotalWeightVgm ?? 0) > 0;
|
||||
if (b.freightType === "BULK" && !perItemBulk && typeIds.length) {
|
||||
const smart = this.smartBulkNeed(b, wagonDims, ledger, leg, rank);
|
||||
const smart = this.smartBulkNeed(b, wagonDims, ledger, leg, rank, typeIds);
|
||||
if (smart) {
|
||||
for (const part of smart.perType) {
|
||||
ledger.consume([part.wagonTypeId], part.wagons, leg);
|
||||
@@ -5168,9 +5180,19 @@ export class BookingBatchService implements OnModuleInit {
|
||||
stock: WagonStockLedger,
|
||||
leg: CorridorLeg,
|
||||
scarcityRank: Map<string, number>,
|
||||
/**
|
||||
* Wagon-type ids this booking may ride, from {@link loadAllowedWagonTypeIds}
|
||||
* — NEVER from `booking.cargoType.wagonTypes`. The batch pool finders
|
||||
* deliberately do not join that relation (hot path), so on a pool entity
|
||||
* it is always empty; resolving through it made every PER_TON bulk booking
|
||||
* unseatable — no whole fit and no partial offer, silently READY forever
|
||||
* (the S-2026-00020 / BK-2026-000036 incident).
|
||||
*/
|
||||
wagonTypeIds: readonly string[],
|
||||
): { need: Capacity; perType: Array<{ wagonTypeId: string; wagons: number }> } | null {
|
||||
const options = this.allowedDimsWithTypes(booking, wagonDims)
|
||||
.filter((o): o is { wagonTypeId: string; dims: PerWagonDims } => o.wagonTypeId != null)
|
||||
const options = [...new Set(wagonTypeIds)]
|
||||
.map((wagonTypeId) => ({ wagonTypeId, dims: wagonDims.byWagonTypeId.get(wagonTypeId) }))
|
||||
.filter((o): o is { wagonTypeId: string; dims: PerWagonDims } => o.dims != null)
|
||||
.map((o) => ({
|
||||
...o,
|
||||
free: stock.availableFor([o.wagonTypeId], leg),
|
||||
|
||||
@@ -22,17 +22,21 @@ describe('BookingBatchService.smartBulkNeed', () => {
|
||||
s: WagonStockLedger,
|
||||
l: { fromEdge: number; toEdge: number },
|
||||
r: Map<string, number>,
|
||||
ids: readonly string[],
|
||||
) => { need: { wagons: number }; perType: Array<{ wagonTypeId: string; wagons: number }> } | null;
|
||||
}
|
||||
).smartBulkNeed(booking, wagonDims, stock, { fromEdge: 0, toEdge: 1 }, rank);
|
||||
).smartBulkNeed(booking, wagonDims, stock, { fromEdge: 0, toEdge: 1 }, rank, allowedIds);
|
||||
|
||||
const nw5 = { id: 'wt-nw5', capacityTons: 70 };
|
||||
const pw2 = { id: 'wt-pw2', capacityTons: 70 };
|
||||
// Shaped like a BATCH POOL entity: cargoType WITHOUT the wagonTypes
|
||||
// relation (the pool query never joins it) — allowed types must come from
|
||||
// the ids parameter, or every pool bulk booking reads as unseatable.
|
||||
const perishable = {
|
||||
id: 'cargo-perishable',
|
||||
wagonTypes: [nw5, pw2],
|
||||
tonsPerWagonMap: { [nw5.id]: 30, [pw2.id]: 20 },
|
||||
};
|
||||
const allowedIds = [nw5.id, pw2.id];
|
||||
const wagonDims = {
|
||||
container: { lengthMeters: 14, tareWeightTons: 24, capacityTons: 70 },
|
||||
bulk: { lengthMeters: 14, tareWeightTons: 24, capacityTons: 70 },
|
||||
@@ -113,9 +117,10 @@ describe('BookingBatchService.smartBulkNeed', () => {
|
||||
s: WagonStockLedger,
|
||||
l: { fromEdge: number; toEdge: number },
|
||||
r: Map<string, number>,
|
||||
ids: readonly string[],
|
||||
) => { need: { wagons: number }; perType: Array<{ wagonTypeId: string; wagons: number }> } | null;
|
||||
}
|
||||
).smartBulkNeed(booking(200), wagonDims, stock, { fromEdge: 0, toEdge: 2 }, contested);
|
||||
).smartBulkNeed(booking(200), wagonDims, stock, { fromEdge: 0, toEdge: 2 }, contested, allowedIds);
|
||||
expect(smart).not.toBeNull();
|
||||
expect(smart!.perType).toEqual([{ wagonTypeId: pw2.id, wagons: 10 }]);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user