Files
edr-platform/apps/edr-freight-api/src/scripts/seed-gate-pass-train-scenarios.ts
2026-07-17 09:16:35 +00:00

623 lines
24 KiB
TypeScript

import 'reflect-metadata';
import { config } from 'dotenv';
import { resolve } from 'path';
import { WagonStatus } from '@edr/types';
import { In } from 'typeorm';
config({ path: resolve(__dirname, '../../.env') });
import { AppDataSource } from '../data-source';
import { BookingContainer } from '../modules/bookings/entities/booking-container.entity';
import { Booking } from '../modules/bookings/entities/booking.entity';
import { CompanyProfile, ProfileStatus, ProfileType } from '../modules/companies/entities/company-profile.entity';
import { Company, CompanyKind, CompanyStatus, CompanyType } from '../modules/companies/entities/company.entity';
import { Container } from '../modules/container-management/entities/container.entity';
import { Locomotive } from '../modules/locomotives/entities/locomotive.entity';
import { ContainerType } from '../modules/rule-engine/entities/container-type.entity';
import { ServiceType } from '../modules/rule-engine/entities/service-type.entity';
import { Yard } from '../modules/rule-engine/entities/yard.entity';
import { ImportDjiboutiOperation } from '../modules/train-scheduling/entities/import-djibouti-operation.entity';
import { TrainScheduleBooking } from '../modules/train-schedules/entities/train-schedule-booking.entity';
import { WagonAllocationContainerItem } from '../modules/train-schedules/entities/wagon-allocation-container-item.entity';
import { WagonBookingAllocation } from '../modules/train-schedules/entities/wagon-booking-allocation.entity';
import { TrainSchedule } from '../modules/train-schedules/entities/train-schedule.entity';
import { TrainSetWagon } from '../modules/train-sets/entities/train-set-wagon.entity';
import { TrainSet } from '../modules/train-sets/entities/train-set.entity';
import { WagonType } from '../modules/wagon-types/entities/wagon-type.entity';
import { Wagon } from '../modules/wagons/entities/wagon.entity';
import { WarehouseInventory } from '../modules/warehouses/entities/warehouse-inventory.entity';
import { WarehouseYard } from '../modules/warehouses/entities/warehouse-yard.entity';
import { WarehouseZone } from '../modules/warehouses/entities/warehouse-zone.entity';
import { Warehouse } from '../modules/warehouses/entities/warehouse.entity';
type Direction = 'IMPORT' | 'EXPORT';
type TrainStatus = 'SCHEDULED' | 'ARRIVED';
interface ScenarioTrain {
trainNumber: string;
direction: Direction;
status: TrainStatus;
departureOffsetHours: number;
arrivalOffsetHours: number;
bookings: Array<{
reference: string;
mileVariant: 'FIRST_MILE' | 'LAST_MILE' | 'TERMINAL';
containerNumber: string;
weightTons: number;
}>;
}
const SCENARIOS: ScenarioTrain[] = [
{
trainNumber: 'GP-IMP-ARR-01',
direction: 'IMPORT',
status: 'ARRIVED',
departureOffsetHours: -18,
arrivalOffsetHours: -6,
bookings: [
{ reference: 'GP-IMP-ARR-LM-001', mileVariant: 'LAST_MILE', containerNumber: 'GPIM0000001', weightTons: 22 },
{ reference: 'GP-IMP-ARR-TM-002', mileVariant: 'TERMINAL', containerNumber: 'GPIM0000002', weightTons: 24 },
],
},
{
trainNumber: 'GP-IMP-NARR-01',
direction: 'IMPORT',
status: 'SCHEDULED',
departureOffsetHours: 6,
arrivalOffsetHours: 18,
bookings: [
{ reference: 'GP-IMP-NARR-LM-001', mileVariant: 'LAST_MILE', containerNumber: 'GPIM0000003', weightTons: 21 },
{ reference: 'GP-IMP-NARR-TM-002', mileVariant: 'TERMINAL', containerNumber: 'GPIM0000004', weightTons: 23 },
],
},
{
trainNumber: 'GP-EXP-ARR-01',
direction: 'EXPORT',
status: 'ARRIVED',
departureOffsetHours: -16,
arrivalOffsetHours: -4,
bookings: [
{ reference: 'GP-EXP-ARR-FM-001', mileVariant: 'FIRST_MILE', containerNumber: 'GPEX0000001', weightTons: 20 },
{ reference: 'GP-EXP-ARR-TM-002', mileVariant: 'TERMINAL', containerNumber: 'GPEX0000002', weightTons: 22 },
],
},
{
trainNumber: 'GP-EXP-NARR-01',
direction: 'EXPORT',
status: 'SCHEDULED',
departureOffsetHours: 8,
arrivalOffsetHours: 20,
bookings: [
{ reference: 'GP-EXP-NARR-FM-001', mileVariant: 'FIRST_MILE', containerNumber: 'GPEX0000003', weightTons: 19 },
{ reference: 'GP-EXP-NARR-TM-002', mileVariant: 'TERMINAL', containerNumber: 'GPEX0000004', weightTons: 21 },
],
},
];
const addHours = (date: Date, hours: number): Date => new Date(date.getTime() + hours * 60 * 60 * 1000);
async function main() {
const dataSource = await AppDataSource.initialize();
try {
const seeded = await dataSource.transaction(async (manager) => {
if (await isAlreadySeeded(manager)) {
return null;
}
const refs = await ensureReferences(manager);
const now = new Date();
const result: Array<{ trainNumber: string; bookings: string[] }> = [];
for (const scenario of SCENARIOS) {
const schedule = await seedScenarioTrain(manager, scenario, refs, now);
result.push({
trainNumber: schedule.trainNumber ?? scenario.trainNumber,
bookings: scenario.bookings.map((booking) => booking.reference),
});
}
return result;
});
console.log('Gate-pass train scenario seed complete.');
if (seeded) {
for (const row of seeded) {
console.log(`${row.trainNumber}: ${row.bookings.join(', ')}`);
}
} else {
console.log('Gate-pass train scenarios already seeded; nothing changed.');
}
} finally {
await dataSource.destroy();
}
}
async function isAlreadySeeded(manager: any): Promise<boolean> {
const scheduleRepo = manager.getRepository(TrainSchedule);
const bookingRepo = manager.getRepository(Booking);
const trainNumbers = SCENARIOS.map((scenario) => scenario.trainNumber);
const bookingRefs = SCENARIOS.flatMap((scenario) => scenario.bookings.map((booking) => booking.reference));
const [scheduleCount, bookingCount] = await Promise.all([
scheduleRepo.count({ where: { trainNumber: In(trainNumbers) } }),
bookingRepo.count({ where: { reference: In(bookingRefs) } }),
]);
return scheduleCount === trainNumbers.length && bookingCount === bookingRefs.length;
}
async function ensureReferences(manager: any) {
const yardRepo = manager.getRepository(Yard);
const serviceTypeRepo = manager.getRepository(ServiceType);
const containerTypeRepo = manager.getRepository(ContainerType);
const wagonTypeRepo = manager.getRepository(WagonType);
const companyRepo = manager.getRepository(Company);
const profileRepo = manager.getRepository(CompanyProfile);
const warehouseRepo = manager.getRepository(Warehouse);
const warehouseYardRepo = manager.getRepository(WarehouseYard);
const warehouseZoneRepo = manager.getRepository(WarehouseZone);
const djiboutiYard =
(await yardRepo.findOne({ where: { code: 'NAGAD' } })) ??
(await yardRepo.findOne({ where: { code: 'DJIB_PORT' } })) ??
(await yardRepo.findOne({ where: { country: 'Djibouti' } })) ??
(await yardRepo.save(
yardRepo.create({
code: 'NAGAD',
label: 'Nagad Port',
country: 'Djibouti',
isActive: true,
displayOrder: 90,
}),
));
const ethiopiaYard =
(await yardRepo.findOne({ where: { code: 'INDODE' } })) ??
(await yardRepo.findOne({ where: { code: 'MOJO' } })) ??
(await yardRepo.findOne({ where: { country: 'Ethiopia' } })) ??
(await yardRepo.save(
yardRepo.create({
code: 'INDODE',
label: 'Indode Dry Port',
country: 'Ethiopia',
isActive: true,
displayOrder: 91,
}),
));
const serviceType =
(await serviceTypeRepo.findOne({ where: { code: 'RAIL_CONTAINER' } })) ??
(await serviceTypeRepo.save(
serviceTypeRepo.create({
code: 'RAIL_CONTAINER',
serviceName: 'Rail Container Service',
description: 'Rail container service for gate-pass scenario seed',
canBeBookedAlone: true,
includesFirstMile: false,
includesLastMile: false,
includesCustoms: false,
isActive: true,
displayOrder: 1,
}),
));
const containerType =
(await containerTypeRepo.findOne({ where: { code: '40FT' } })) ??
(await containerTypeRepo.findOne({ where: { isActive: true } })) ??
(await containerTypeRepo.save(
containerTypeRepo.create({
code: '40FT',
label: '40FT',
sizeFt: 40,
isReefer: false,
isOpenTop: false,
isActive: true,
displayOrder: 1,
}),
));
const wagonType =
(await wagonTypeRepo.findOne({ where: { code: 'GP-FLAT' } })) ??
(await wagonTypeRepo.findOne({ where: { supportsContainer: true } })) ??
(await wagonTypeRepo.findOne({ where: { isActive: true } })) ??
(await wagonTypeRepo.save(
wagonTypeRepo.create({
code: 'GP-FLAT',
name: 'Gate Pass Demo Flat Wagon',
capacityTons: 70,
lengthMeters: 14,
supportedLoadTypes: ['CONTAINER'],
isActive: true,
equatedLengthM: 14,
tareWeightTons: 20,
supportsContainer: true,
maxContainerGrossT: 70,
}),
));
const company =
(await companyRepo.findOne({ where: { tin: 'GTPASS001' } })) ??
(await companyRepo.save(
companyRepo.create({
name: 'Gate Pass Scenario Customer',
type: CompanyType.Customer,
kind: CompanyKind.Commercial,
status: CompanyStatus.Active,
tin: 'GTPASS001',
vatNumber: 'GTPASS001',
fanNumber: 'GTPASS0000001',
country: 'Ethiopia',
address: 'Indode Dry Port',
phone: '251900000555',
email: 'gate-pass-scenarios@edr.local',
contactPersonName: 'Gate Pass Tester',
contactPersonPhone: '251900000555',
}),
));
const importerProfile = await ensureProfile(profileRepo, company.id, ProfileType.importer, 'GP-IMP');
const exporterProfile = await ensureProfile(profileRepo, company.id, ProfileType.exporter, 'GP-EXP');
const warehouse =
(await warehouseRepo.findOne({ where: { code: 'INDODE_OPEN' } })) ??
(await warehouseRepo.findOne({ where: {} }));
if (!warehouse) {
throw new Error('No warehouse found. Run the Indode/warehouse seed before gate-pass scenarios.');
}
const warehouseYard = await warehouseYardRepo.findOne({ where: { warehouseId: warehouse.id } });
if (!warehouseYard) {
throw new Error(`No warehouse yard found for ${warehouse.code ?? warehouse.id}.`);
}
const warehouseZone = await warehouseZoneRepo.findOne({ where: { yardId: warehouseYard.id } });
if (!warehouseZone) {
throw new Error(`No warehouse zone found for yard ${warehouseYard.id}.`);
}
return {
djiboutiYard,
ethiopiaYard,
serviceType,
containerType,
wagonType,
company,
importerProfile,
exporterProfile,
warehouse,
warehouseYard,
warehouseZone,
};
}
async function ensureProfile(repo: any, companyId: string, type: ProfileType, reference: string): Promise<CompanyProfile> {
const existing = await repo.findOne({ where: { companyId, type } });
if (existing) return existing;
return repo.save(
repo.create({
companyId,
type,
reference,
status: ProfileStatus.Active,
businessLicense: `${reference}-LICENSE`,
}),
);
}
async function seedScenarioTrain(manager: any, scenario: ScenarioTrain, refs: Awaited<ReturnType<typeof ensureReferences>>, now: Date) {
const locomotiveRepo = manager.getRepository(Locomotive);
const trainSetRepo = manager.getRepository(TrainSet);
const scheduleRepo = manager.getRepository(TrainSchedule);
const trainSetWagonRepo = manager.getRepository(TrainSetWagon);
const wagonRepo = manager.getRepository(Wagon);
const departure = addHours(now, scenario.departureOffsetHours);
const arrival = addHours(now, scenario.arrivalOffsetHours);
const isArrived = scenario.status === 'ARRIVED';
const originYard = scenario.direction === 'IMPORT' ? refs.djiboutiYard : refs.ethiopiaYard;
const destinationYard = scenario.direction === 'IMPORT' ? refs.ethiopiaYard : refs.djiboutiYard;
const totalWeightTons = scenario.bookings.reduce((sum, booking) => sum + booking.weightTons, 0);
const locomotive =
(await locomotiveRepo.findOne({ where: { code: 'GP-DEMO-LOCO' } })) ??
(await locomotiveRepo.save(
locomotiveRepo.create({
code: 'GP-DEMO-LOCO',
name: 'Gate Pass Scenario Locomotive',
locomotiveType: 'DIESEL',
maxPullWeightTons: 4200,
maxTrainLengthMeters: 760,
status: 'AVAILABLE',
currentYardId: originYard.id,
}),
));
let schedule = await scheduleRepo.findOne({ where: { trainNumber: scenario.trainNumber } });
let trainSet: TrainSet | null = schedule?.trainSetId
? await trainSetRepo.findOne({ where: { id: schedule.trainSetId } })
: null;
if (!trainSet) {
trainSet = await trainSetRepo.save(
trainSetRepo.create({
locomotiveId: locomotive.id,
totalWeightTons,
totalLengthMeters: scenario.bookings.length * 14,
wagonCount: scenario.bookings.length,
status: isArrived ? 'COMPLETED' : 'ASSIGNED',
}),
);
} else {
await trainSetRepo.update(trainSet.id, {
locomotiveId: locomotive.id,
totalWeightTons,
totalLengthMeters: scenario.bookings.length * 14,
wagonCount: scenario.bookings.length,
status: isArrived ? 'COMPLETED' : 'ASSIGNED',
});
}
if (!trainSet) {
throw new Error(`Could not create train set for ${scenario.trainNumber}`);
}
const trainSetId = trainSet.id;
if (!schedule) {
schedule = scheduleRepo.create({ trainNumber: scenario.trainNumber });
}
Object.assign(schedule, {
trainSetId,
originStationId: originYard.id,
destinationStationId: destinationYard.id,
scheduledDepartureDate: departure,
scheduledArrivalDate: arrival,
actualDepartureAt: isArrived ? departure : null,
actualArrivalAt: isArrived ? arrival : null,
status: scenario.status,
direction: scenario.direction,
maxWagons: 53,
bookingWindowStatus: 'CLOSED',
});
schedule = await scheduleRepo.save(schedule);
for (const [index, bookingSpec] of scenario.bookings.entries()) {
const sequenceNo = index + 1;
const wagon = await ensureWagon(manager, scenario, sequenceNo, refs.wagonType.id, originYard.id, schedule.id);
const trainSetWagon = await ensureTrainSetWagon(
trainSetWagonRepo,
trainSetId,
refs.wagonType.id,
wagon.id,
sequenceNo,
bookingSpec.weightTons,
isArrived,
);
await wagonRepo.update(wagon.id, { trainSetWagonId: trainSetWagon.id });
const booking = await ensureBooking(manager, scenario, bookingSpec, refs, departure, now, schedule.id);
const bookingContainer = await ensureBookingContainer(manager, booking.id, refs.containerType.id, bookingSpec);
const allocation = await ensureAllocation(manager, trainSetWagon.id, booking.id, bookingSpec.weightTons, isArrived, now);
const container = await ensureContainer(manager, booking.id, bookingContainer.id, allocation.id, wagon.id, sequenceNo, bookingSpec, refs.containerType.id, isArrived);
await ensureContainerItem(manager, allocation.id, bookingContainer.id, container.id, refs.containerType.id, sequenceNo, bookingSpec);
await ensureScheduleBooking(manager, schedule.id, booking.id);
if (scenario.direction === 'EXPORT') {
await ensureExportInventory(manager, refs, booking.id, container.id, bookingSpec.weightTons, isArrived, now);
}
}
if (scenario.direction === 'IMPORT') {
await ensureImportOperation(manager, schedule.id, scenario, departure, isArrived);
}
return schedule;
}
async function ensureWagon(manager: any, scenario: ScenarioTrain, sequenceNo: number, wagonTypeId: string, yardId: string, scheduleId: string): Promise<Wagon> {
const repo = manager.getRepository(Wagon);
const wagonNumber = `${scenario.trainNumber}-W${String(sequenceNo).padStart(2, '0')}`;
const existing = await repo.findOne({ where: { wagonNumber } });
const values = {
wagonNumber,
wagonTypeId,
trainId: null,
sequenceNumber: sequenceNo,
status: WagonStatus.Assigned,
currentYardId: yardId,
currentTrainScheduleId: scheduleId,
notes: 'Gate-pass scenario seed wagon',
};
return repo.save(repo.create({ ...(existing ?? {}), ...values }));
}
async function ensureTrainSetWagon(repo: any, trainSetId: string, wagonTypeId: string, wagonId: string, sequenceNo: number, weightTons: number, isArrived: boolean): Promise<TrainSetWagon> {
const existing = await repo.findOne({ where: { trainSetId, sequenceNo } });
return repo.save(
repo.create({
...(existing ?? {}),
trainSetId,
wagonTypeId,
physicalWagonId: wagonId,
sequenceNo,
capacityTons: 70,
lengthMeters: 14,
assignedWeightTons: weightTons,
status: isArrived ? 'DEPARTED' : 'LOADED',
}),
);
}
async function ensureBooking(manager: any, scenario: ScenarioTrain, bookingSpec: ScenarioTrain['bookings'][number], refs: Awaited<ReturnType<typeof ensureReferences>>, departure: Date, now: Date, scheduleId: string): Promise<Booking> {
const repo = manager.getRepository(Booking);
const originYard = scenario.direction === 'IMPORT' ? refs.djiboutiYard : refs.ethiopiaYard;
const destinationYard = scenario.direction === 'IMPORT' ? refs.ethiopiaYard : refs.djiboutiYard;
const existing = await repo.findOne({ where: { reference: bookingSpec.reference } });
const profile = scenario.direction === 'IMPORT' ? refs.importerProfile : refs.exporterProfile;
const hasFirstMile = bookingSpec.mileVariant === 'FIRST_MILE';
const hasLastMile = bookingSpec.mileVariant === 'LAST_MILE';
return repo.save(
repo.create({
...(existing ?? {}),
reference: bookingSpec.reference,
companyId: refs.company.id,
companyProfileId: profile.id,
originYardId: originYard.id,
destinationYardId: destinationYard.id,
serviceTypeId: refs.serviceType.id,
status: scenario.status === 'ARRIVED' ? 'IN_TRANSIT' : 'PAID',
paymentStatus: 'PAID',
scheduledDate: departure,
estimatedShipmentDate: departure,
contractType: 'SPOT',
equipmentReturn: 'TERMINAL',
paymentCurrency: 'ETB',
totalAmount: 0,
isGovernment: false,
tradeDirection: scenario.direction,
freightType: 'CONTAINER',
cargoTypeId: null,
cargoFreeText: `${scenario.direction} gate-pass scenario ${bookingSpec.mileVariant.toLowerCase().replace('_', ' ')}`,
cargoTotalWeightVgm: bookingSpec.weightTons * 1000,
firstMilePickupAddress: hasFirstMile ? 'Customer factory pickup - Addis Ababa' : null,
firstMilePickupLat: hasFirstMile ? 9.03 : null,
firstMilePickupLng: hasFirstMile ? 38.74 : null,
lastMileDeliveryAddress: hasLastMile ? 'Customer warehouse delivery - Addis Ababa' : null,
lastMileDeliveryLat: hasLastMile ? 8.98 : null,
lastMileDeliveryLng: hasLastMile ? 38.8 : null,
trainScheduleId: scheduleId,
schedulingStatus: scenario.status === 'ARRIVED' ? 'DISPATCHED' : 'SCHEDULED',
scheduledAt: now,
wagonsRequired: 1,
}),
);
}
async function ensureBookingContainer(manager: any, bookingId: string, containerTypeId: string, bookingSpec: ScenarioTrain['bookings'][number]): Promise<BookingContainer> {
const repo = manager.getRepository(BookingContainer);
const existing = await repo.findOne({ where: { bookingId } });
return repo.save(
repo.create({
...(existing ?? {}),
bookingId,
containerTypeId,
containerNumber: bookingSpec.containerNumber,
containerSize: '40',
quantity: 1,
hazardousQuantity: 0,
reeferQuantity: 0,
vgmPerUnitTons: bookingSpec.weightTons,
totalVgmTons: bookingSpec.weightTons,
wagonsRequired: 1,
weightLimitRuleId: null,
isOverweight: false,
overweightExcessTons: null,
}),
);
}
async function ensureAllocation(manager: any, trainSetWagonId: string, bookingId: string, weightTons: number, isArrived: boolean, now: Date): Promise<WagonBookingAllocation> {
const repo = manager.getRepository(WagonBookingAllocation);
const existing = await repo.findOne({ where: { trainSetWagonId, bookingId } });
return repo.save(
repo.create({
...(existing ?? {}),
trainSetWagonId,
bookingId,
allocatedWeightTons: weightTons,
loadType: 'CONTAINER',
status: isArrived ? 'DEPARTED' : 'LOADED',
confirmedAt: now,
}),
);
}
async function ensureContainer(manager: any, bookingId: string, bookingContainerId: string, allocationId: string, wagonId: string, position: number, bookingSpec: ScenarioTrain['bookings'][number], containerTypeId: string, isArrived: boolean): Promise<Container> {
const repo = manager.getRepository(Container);
const existing = await repo.findOne({ where: { containerNumber: bookingSpec.containerNumber } });
return repo.save(
repo.create({
...(existing ?? {}),
containerNumber: bookingSpec.containerNumber,
containerTypeId,
wagonId,
position,
tareWeight: 3800,
maxGrossWeight: 30480,
sealNumber: `SEAL-${bookingSpec.containerNumber}`,
status: isArrived ? 'IN_TRANSIT' : 'LOADED',
bookingId,
wagonBookingAllocationId: allocationId,
bookingContainerId,
}),
);
}
async function ensureContainerItem(manager: any, allocationId: string, bookingContainerId: string, containerId: string, containerTypeId: string, position: number, bookingSpec: ScenarioTrain['bookings'][number]): Promise<void> {
const repo = manager.getRepository(WagonAllocationContainerItem);
await repo.delete({ wagonBookingAllocationId: allocationId });
await repo.save(
repo.create({
wagonBookingAllocationId: allocationId,
bookingContainerId,
containerId,
containerNumber: bookingSpec.containerNumber,
containerTypeId,
positionOnWagon: position,
sealNumber: `SEAL-${bookingSpec.containerNumber}`,
chassisNumber: `CHS-${bookingSpec.containerNumber}`,
grossWeightTons: bookingSpec.weightTons,
}),
);
}
async function ensureScheduleBooking(manager: any, scheduleId: string, bookingId: string): Promise<void> {
const repo = manager.getRepository(TrainScheduleBooking);
const existing = await repo.findOne({ where: { bookingId } });
await repo.save(repo.create({ ...(existing ?? {}), trainScheduleId: scheduleId, bookingId }));
}
async function ensureExportInventory(manager: any, refs: Awaited<ReturnType<typeof ensureReferences>>, bookingId: string, containerId: string, weightTons: number, isArrived: boolean, now: Date): Promise<void> {
const repo = manager.getRepository(WarehouseInventory);
const existing = await repo.findOne({ where: { bookingId } });
await repo.save(
repo.create({
...(existing ?? {}),
warehouseId: refs.warehouse.id,
yardId: refs.warehouseYard.id,
zoneId: refs.warehouseZone.id,
bookingId,
containerId,
quantity: 1,
weight: weightTons * 1000,
status: 'LOADED',
inspectionStatus: 'PASSED',
arrivedAt: addHours(now, -24),
inspectedAt: addHours(now, -22),
readyForLoadingAt: addHours(now, -20),
loadedAt: isArrived ? addHours(now, -16) : null,
notes: '[GP-SCENARIO] Export train gate-pass scenario inventory',
}),
);
}
async function ensureImportOperation(manager: any, scheduleId: string, scenario: ScenarioTrain, departure: Date, isArrived: boolean): Promise<void> {
const repo = manager.getRepository(ImportDjiboutiOperation);
const existing = await repo.findOne({ where: { trainScheduleId: scheduleId } });
await repo.save(
repo.create({
...(existing ?? {}),
trainScheduleId: scheduleId,
documents: existing?.documents ?? {},
gatepassGrantedAt: null,
readyForLoadingAt: null,
loadedOnTrainAt: null,
departedFromDjiboutiAt: isArrived ? departure : null,
loadListGeneratedAt: null,
performedBy: 'Gate Pass Scenario Seeder',
notes: `[GP-SCENARIO] ${scenario.trainNumber}; fill gate-pass dates during testing`,
}),
);
}
main().catch((error) => {
console.error('Gate-pass train scenario seed failed:', error);
process.exit(1);
});