- Removed maxWagonsPerTrain from WagonType entity and related DTOs. - Updated containerWagonsForLines function to calculate required wagons based on container lines more accurately. - Added unit tests for containerWagonsForLines to ensure correct calculations. - Adjusted related services and scripts to reflect the removal of maxWagonsPerTrain. - Enhanced booking and contract components to use new status labels for better user experience. - Implemented validation for unique container numbers in shipment forms.
4.0 KiB
Priority & Batch Window Flow (Import, Freight)
Export = no batch, no priority. Pure first-come-first-served (booking-batch.service.ts:462-467, 625-628). Everything below is import only.
Step by step
1. Booking submitted → priority score computed
booking-transition.service.ts:110-111,196-197 → booking-pricing.service.ts:403-407 computeSubmitPriorityScore() → rule-engine.service.ts:118.
- Government booking:
+50,000(government-priority.constants.ts:2, appliedrule-engine.service.ts:201) - Plus cargo/weight modifiers
- Stored on
booking.priorityScore
2. Window opens (PRE_WINDOW → OPEN)
Cron tick every 10s: booking-window.service.ts:63 → advanceImport → booking-window.service.ts:232-251.
Times computed by computeImportWindowTimes (batch-window.util.ts:248-283).
3. Customers book during OPEN Booking lands as:
- Commercial →
FULLY_EXECUTED - Government →
APPROVED/PAID(skips contract flow)
4. Window closes (OPEN → DOC_REVIEW)
booking-window.service.ts:254-268. Staff review docs for docReviewMinutes.
5. Doc review ends
Staff completeDocReview() (booking-window.service.ts:124-159) or timeout → booking-window.service.ts:270-295.
Before batch runs: expireUnacceptedForRouteDay (booking-batch.service.ts:1853-1883) kills never-accepted bookings so they can't compete.
6. Batch fill runs
processRouteDay → fillRouteDay (booking-batch.service.ts:1138-1319), or single-schedule fillSchedule (:1018-1128).
- Pool pulled pre-sorted:
findBatchPool/findBatchPoolByCorridorDay(bookings.repository.ts:991-1008, 1055-1083)ORDER BY is_government DESC, priority_score DESC, fully_executed_at ASC, created_at ASC - Consolidated pairs grouped as one atomic unit:
groupConsolidatedPool(:1962-1987) — never split. - Greedy placement, earliest-departing fitting train first: loop at
:1218-1306. - No fit + government booking →
preemptForGovernment(:1891-1910): bumps lowest-priorityScorecommercial victim first, only if legs overlap (:1920). - No fit + commercial import (GENERAL/ONE_TIME) → maybe partial "split" offer:
maybeOfferPartial/isSplitEligible(:1326-1370). - Still no fit → stays pooled,
notifier.unplaced(:1278-1280).
7. Placed bookings get reserved/allocated
- Commercial:
reserve()(:1673-1703) →SELECTED_FOR_BATCH, payment deadline set, DOC_REVIEW→PAYMENT (booking-window.service.ts:275-294). - Government:
allocate()directly (:1706-1746), no payment step.
8. Payment phase ends
booking-window.service.ts:297-309 → settleDueReservations → settleReserved (:1437-1491):
- paid → allocated
- unpaid → expired, capacity freed
Then concludeCycle (:315-373):
- Train full →
DONE+ auto-finalize (:320-329) - Not full → reopen same/next day (
nextCycleOpensAt/ office hours,:331-372,batch-window.util.ts:217-224) orDONEif no cycle fits before departure.
9. Backstop
settleOverdueReservations (booking-window.service.ts:388-406) catches any reservation whose deadline passed outside the normal tick.
Phase enum
PRE_WINDOW → OPEN → DOC_REVIEW → PAYMENT → (reopen PRE_WINDOW | DONE)
(booking-window.config.ts:27-34)
What decides priority
is_government— always first, both in SQL sort andcompareSchedulingPriorityutil (compare-scheduling-priority.util.ts:9-23)priority_scoreDESC (rule engine: government bonus + cargo/weight modifiers)fully_executed_atASC (earlier wins)created_atASC
Edge cases
- Government preemption only bumps if legs overlap; picks lowest-priority victim first.
- Consolidated pairs are both-or-neither, never split (
:1326-1334, 1793). - Only GENERAL/ONE_TIME import bookings are eligible for partial "split" offers.
- Per-unit try/catch around reserve — one failure can't cause silent trickle/stagger allocation (comment at
:1283-1288). - Each train freezes its own rule snapshot at window-open time, not live config (
booking-window.service.ts:85-93).