update joins in repository services to use entity classes and enhance booking form with hazardous/reefer toggles

This commit is contained in:
Marshal
2026-07-04 04:23:10 +00:00
parent 3e94aa08f1
commit 2c0e115c14
3 changed files with 120 additions and 42 deletions

View File

@@ -138,6 +138,11 @@ export class ContractBookingService {
// no override. Checked before any row is written.
if (freightType === 'CONTAINER') {
await this.assertWithinMaxCapacity(contract, dto);
// 20ft weight-pairing gate at CREATION: two 20ft on a wagon must differ
// ≤ the cap, and drawdown bookings never pass through submit — so this is
// their only chance to hard-block an unbalanceable set. Entry order is
// irrelevant (the check sorts by weight before pairing).
await this.assert20ftPairableAtCreate(dto);
}
// Denormalize route/direction/freight onto the booking for the scheduling engine.
@@ -761,6 +766,35 @@ export class ContractBookingService {
}
}
/**
* Hard-block booking creation when the 20ft container weights cannot be
* balanced onto wagons (pair diff over the global cap). Same rule the
* shipment-form preview reports as `pairingErrors`, enforced server-side.
*/
private async assert20ftPairableAtCreate(
dto: CreateBookingUnderContractDto,
): Promise<void> {
const twentyFtUnits = (dto.containers ?? [])
.filter((line) => (line.containerSize ?? '').includes('20'))
.flatMap((line, lineIdx) =>
(line.units ?? []).map((u, idx) => ({
label: u.containerNumber || `20ft-${lineIdx + 1}.${idx + 1}`,
grossWeightTons: Number(u.vgmTons ?? 0),
})),
);
if (twentyFtUnits.length < 2) return;
const maxDiff = await this.max20ftPairDiffTons();
const violations = validate20ftWeightPairing(twentyFtUnits, maxDiff);
if (violations.length) {
throw new BadRequestException(
`Cannot create booking — 20ft containers cannot be paired on wagons: ${violations
.map((v) => v.message)
.join(' ')}`,
);
}
}
private async max20ftPairDiffTons(): Promise<number> {
const row = await this.dataSource
.getRepository(TrainSchedulingGlobalRules)