mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 01:20:55 +00:00
split export
This commit is contained in:
@@ -25,6 +25,7 @@ import { validate20ftWeightPairing } from '../bookings/container-pairing.util';
|
||||
import { TrainSchedulingGlobalRules } from '../train-scheduling/entities/train-scheduling-global-rules.entity';
|
||||
import { TrainSchedulingService } from '../train-scheduling/train-scheduling.service';
|
||||
import { BookingBatchService } from '../train-scheduling/booking-batch.service';
|
||||
import { eatDay } from '../train-scheduling/batch-window.util';
|
||||
import { wagonsPerUnitForSize } from '../rule-engine/container-type.util';
|
||||
import { ContainerTypesService } from '../rule-engine/services/container-types.service';
|
||||
import { RuleEngineService } from '../rule-engine/rule-engine.service';
|
||||
@@ -54,6 +55,18 @@ export interface CreateBookingUnderContractResult {
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Outstanding split remainder of a contract: what was booked in the first split
|
||||
* booking's pre-split snapshot MINUS everything currently booked. Container
|
||||
* contracts report per size; bulk reports one tonnage figure. `null` when the
|
||||
* contract has no live split chain. Consumed by the remainder-placement engine
|
||||
* to size the auto-created remainder booking.
|
||||
*/
|
||||
export type SplitOutstanding = {
|
||||
bySize: Map<string, { total: number; outstanding: number }>;
|
||||
bulk: { total: number; outstanding: number } | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* The single create path for shipment bookings under a contract.
|
||||
*
|
||||
@@ -966,9 +979,11 @@ export class ContractBookingService {
|
||||
* (CANCELLED / REJECTED / EXPIRED) release their share. Null when the
|
||||
* contract has no live split booking.
|
||||
*/
|
||||
private async splitOutstanding(
|
||||
contract: Contract,
|
||||
): Promise<{ bySize: Map<string, { total: number; outstanding: number }>; bulk: { total: number; outstanding: number } | null } | null> {
|
||||
/**
|
||||
* Public: the remainder-placement engine reads this to size the auto-created
|
||||
* remainder booking. Returns `null` when there is no live split chain.
|
||||
*/
|
||||
async splitOutstanding(contract: Contract): Promise<SplitOutstanding | null> {
|
||||
const first = await this.dataSource
|
||||
.getRepository(Booking)
|
||||
.createQueryBuilder('b')
|
||||
@@ -1015,6 +1030,25 @@ export class ContractBookingService {
|
||||
const probe = await this.buildExportProbe(contract, route, dto, yards);
|
||||
const report = await this.bookingBatchService.exportSpaceReport(probe);
|
||||
if (report.scheduleId) return;
|
||||
|
||||
// With export split ON a booking no longer has to ride ONE train whole: the
|
||||
// largest fitting part is offered and the leftover is rebooked on the next
|
||||
// train. Rejecting on the single-train fit here would block exactly the
|
||||
// bookings the split exists to serve — including the auto-created remainder,
|
||||
// which by definition did not fit the train it was split off. Fall back to
|
||||
// the day total: unbookable only when NO export train that day has room.
|
||||
if (process.env.FREIGHT_EXPORT_SPLIT === 'true') {
|
||||
const fitting = await this.bookingBatchService.fittingTrainsForDay(
|
||||
probe,
|
||||
eatDay(new Date(dto.scheduledDate)),
|
||||
'EXPORT',
|
||||
);
|
||||
if (fitting.length > 0) return;
|
||||
throw new BadRequestException(
|
||||
'No export train on this day has space left — pick another shipment day.',
|
||||
);
|
||||
}
|
||||
|
||||
throw new BadRequestException(
|
||||
report.fullMessage ?? 'Not enough train space for this day.',
|
||||
);
|
||||
@@ -1636,6 +1670,8 @@ export class ContractBookingService {
|
||||
isGovernment: contract.isGovernment,
|
||||
shippingLineId: null,
|
||||
contractRouteId: route?.id ?? null,
|
||||
originYardId: route?.originYardId ?? null,
|
||||
destinationYardId: route?.destinationYardId ?? null,
|
||||
cargoTotalWeightVgm: this.resolveBulkTons(dto),
|
||||
firstMilePickupAddress: contract.firstMilePickupAddress ?? null,
|
||||
lastMileDeliveryAddress: contract.lastMileDeliveryAddress ?? null,
|
||||
|
||||
Reference in New Issue
Block a user