mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 02:30:55 +00:00
feat: full wagon cancel, leg board, wagon dates
feat(freight): editable train leg times, SL invoice payer
This commit is contained in:
@@ -10,6 +10,8 @@ import { In, Repository } from "typeorm";
|
||||
import { BookingPricingService } from "../bookings/booking-pricing.service";
|
||||
import { BookingTransitionService } from "../bookings/booking-transition.service";
|
||||
import { BookingsService } from "../bookings/bookings.service";
|
||||
import type { Container20ftUnit } from "../bookings/container-pairing.util";
|
||||
import { ContainerValidationService } from "../bookings/container-validation.service";
|
||||
import { BookingContainer } from "../bookings/entities/booking-container.entity";
|
||||
import { BookingContainerUnit } from "../bookings/entities/booking-container-unit.entity";
|
||||
import { Booking } from "../bookings/entities/booking.entity";
|
||||
@@ -57,8 +59,35 @@ export class ShippingLineBookingCompletionService {
|
||||
private readonly trainSchedulingService: TrainSchedulingService,
|
||||
private readonly bookingBatchService: BookingBatchService,
|
||||
private readonly creditsService: ShippingLineCreditsService,
|
||||
private readonly containerValidationService: ContainerValidationService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 20ft weight-pairing check over the completion payload — the same rule the
|
||||
* customer shipment form enforces (`max20ftPairWeightDiffTons`, default 10t):
|
||||
* two 20ft sharing a wagon must be within the cap. Preview surfaces the
|
||||
* messages; completion hard-blocks on them. Runs off the DTO so nothing is
|
||||
* persisted before the check passes.
|
||||
*/
|
||||
private async pairingViolationMessages(
|
||||
dto: CompleteShippingLineBookingDto,
|
||||
): Promise<string[]> {
|
||||
const units: Container20ftUnit[] = [];
|
||||
for (const line of dto.containers ?? []) {
|
||||
const containerType = await this.resolveContainerType(line);
|
||||
if (containerType.sizeFt !== 20) continue;
|
||||
(line.units ?? []).forEach((u, idx) =>
|
||||
units.push({
|
||||
label: u.containerNumber || `20ft-${idx + 1}`,
|
||||
grossWeightTons: Number(u.vgmTons ?? 0),
|
||||
}),
|
||||
);
|
||||
}
|
||||
const violations =
|
||||
await this.containerValidationService.validate20ftPairingUnits(units);
|
||||
return violations.map((v) => v.message);
|
||||
}
|
||||
|
||||
/** Same session→owner resolution every shipping-line entry point uses. */
|
||||
private async requireShippingLine(userId: string) {
|
||||
const shippingLine =
|
||||
@@ -193,6 +222,17 @@ export class ShippingLineBookingCompletionService {
|
||||
);
|
||||
}
|
||||
|
||||
// Unbalanced 20ft pairs can never be planned onto wagons — refuse before
|
||||
// any cargo/credit write below. Same block the contract path applies.
|
||||
if (booking.freightType === "CONTAINER") {
|
||||
const pairing = await this.pairingViolationMessages(dto);
|
||||
if (pairing.length) {
|
||||
throw new BadRequestException(
|
||||
`Cannot complete booking — 20ft containers cannot be paired on wagons: ${pairing.join(" ")}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Completion is booking time. A lane with trains DEDICATED to this line
|
||||
// has no window concept at all: the line books whenever it wants until the
|
||||
// train's close offset. Only a lane with no dedicated train falls back to
|
||||
@@ -526,11 +566,21 @@ export class ShippingLineBookingCompletionService {
|
||||
computed.appliedModifiers,
|
||||
);
|
||||
|
||||
// Pairing is reported, not thrown: the confirm modal shows it next to the
|
||||
// price (as the customer form does) and disables confirm; /complete
|
||||
// hard-blocks the same payload.
|
||||
const pairingErrors =
|
||||
booking.freightType === "CONTAINER"
|
||||
? await this.pairingViolationMessages(dto)
|
||||
: [];
|
||||
|
||||
return {
|
||||
totalAmount: computed.totalAmount,
|
||||
currency: computed.currency,
|
||||
lineItems: computed.lineItems,
|
||||
warnings: computed.warnings,
|
||||
overweightLines: computed.overweightLines,
|
||||
pairingErrors,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user