import 'reflect-metadata'; import { config } from 'dotenv'; import { resolve } from 'path'; config({ path: resolve(__dirname, '../../.env') }); import { NestFactory } from '@nestjs/core'; import { DataSource } from 'typeorm'; import { AppModule } from '../app.module'; import { Booking } from '../modules/bookings/entities/booking.entity'; import { Locomotive } from '../modules/locomotives/entities/locomotive.entity'; import { CargoType } from '../modules/rule-engine/entities/cargo-type.entity'; import { ServiceType } from '../modules/rule-engine/entities/service-type.entity'; import { Yard } from '../modules/rule-engine/entities/yard.entity'; import { TrainSchedule } from '../modules/train-schedules/entities/train-schedule.entity'; import { TrainScheduleBooking } from '../modules/train-schedules/entities/train-schedule-booking.entity'; import { TrainSet } from '../modules/train-sets/entities/train-set.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'; const DEMO_TRAINS = [ { trainNumber: 'ICD-DEMO-EXP-DJ-01', bookingRefs: ['ICD-DEMO-EXP-001', 'ICD-DEMO-EXP-002', 'ICD-DEMO-EXP-003'], arrivalOffsetHours: 1, }, { trainNumber: 'ICD-DEMO-EXP-DJ-02', bookingRefs: ['ICD-DEMO-EXP-004', 'ICD-DEMO-EXP-005', 'ICD-DEMO-EXP-006'], arrivalOffsetHours: 2, }, ]; async function main() { const app = await NestFactory.createApplicationContext(AppModule, { logger: ['error', 'warn', 'log'], }); try { const dataSource = app.get(DataSource); const yardRepo = dataSource.getRepository(Yard); const serviceTypeRepo = dataSource.getRepository(ServiceType); const cargoTypeRepo = dataSource.getRepository(CargoType); const warehouseRepo = dataSource.getRepository(Warehouse); const warehouseYardRepo = dataSource.getRepository(WarehouseYard); const warehouseZoneRepo = dataSource.getRepository(WarehouseZone); const bookingRepo = dataSource.getRepository(Booking); const inventoryRepo = dataSource.getRepository(WarehouseInventory); const locomotiveRepo = dataSource.getRepository(Locomotive); const trainSetRepo = dataSource.getRepository(TrainSet); const scheduleRepo = dataSource.getRepository(TrainSchedule); const scheduleBookingRepo = dataSource.getRepository(TrainScheduleBooking); const originYard = (await yardRepo.findOne({ where: { code: 'MOJO' } })) ?? (await yardRepo.findOne({ where: { country: 'Ethiopia' } })); const destinationYard = (await yardRepo.findOne({ where: { code: 'DJIB_PORT' } })) ?? (await yardRepo.findOne({ where: { code: 'DJIBOUTI' } })) ?? (await yardRepo.findOne({ where: { country: 'Djibouti' } })); const serviceType = (await serviceTypeRepo.findOne({ where: { code: 'RAIL_CONTAINER' } })) ?? (await serviceTypeRepo.findOne({ where: { isActive: true } })); const cargoType = await cargoTypeRepo.findOne({ where: { isActive: true } }); const warehouse = await warehouseRepo.findOne({ where: { code: 'INDODE_OPEN' } }); const warehouseYard = warehouse ? await warehouseYardRepo.findOne({ where: { warehouseId: warehouse.id } }) : null; const warehouseZone = warehouseYard ? await warehouseZoneRepo.findOne({ where: { yardId: warehouseYard.id } }) : null; const missing = [ !originYard ? 'Ethiopian origin yard' : '', !destinationYard ? 'Djibouti destination yard' : '', !serviceType ? 'service type' : '', !warehouse ? 'INDODE_OPEN warehouse' : '', !warehouseYard ? 'warehouse yard' : '', !warehouseZone ? 'warehouse zone' : '', ].filter(Boolean); if (missing.length) { throw new Error(`Cannot seed export Djibouti interchange demo, missing: ${missing.join(', ')}`); } const locomotive = (await locomotiveRepo.findOne({ where: { code: 'ICD-DEMO-LOCO' } })) ?? (await locomotiveRepo.save( locomotiveRepo.create({ code: 'ICD-DEMO-LOCO', name: 'Interchange Demo Locomotive', maxPullWeightTons: 4000, }), )); const now = Date.now(); const seededSchedules: TrainSchedule[] = []; for (const [trainIndex, demo] of DEMO_TRAINS.entries()) { const existingSchedule = await scheduleRepo.findOne({ where: { trainNumber: demo.trainNumber } }); if (existingSchedule) { console.log(`Export Djibouti interchange demo already seeded: ${demo.trainNumber}`); console.log(`Schedule ID: ${existingSchedule.id}`); seededSchedules.push(existingSchedule); continue; } const arrival = new Date(now - demo.arrivalOffsetHours * 60 * 60 * 1000); const departure = new Date(arrival.getTime() - 5 * 60 * 60 * 1000); const trainSet = await trainSetRepo.save( trainSetRepo.create({ locomotiveId: locomotive.id, totalWeightTons: 700 + trainIndex * 80, totalLengthMeters: 360 + trainIndex * 20, wagonCount: 12 + trainIndex, status: 'COMPLETED', }), ); const schedule = await scheduleRepo.save( scheduleRepo.create({ trainSetId: trainSet.id, originStationId: originYard!.id, destinationStationId: destinationYard!.id, scheduledDepartureDate: departure, scheduledArrivalDate: arrival, actualArrivalAt: arrival, status: 'ARRIVED' as TrainSchedule['status'], trainNumber: demo.trainNumber, direction: 'EXPORT', }), ); for (const [bookingIndex, reference] of demo.bookingRefs.entries()) { const weight = 5200 + trainIndex * 600 + bookingIndex * 800; const booking = await bookingRepo.save( bookingRepo.create({ reference, originYardId: originYard!.id, destinationYardId: destinationYard!.id, serviceTypeId: serviceType!.id, status: 'IN_TRANSIT', paymentStatus: 'PAID', scheduledDate: new Date(), contractType: 'SPOT', equipmentReturn: 'TERMINAL', paymentCurrency: 'ETB', totalAmount: 0, isGovernment: false, tradeDirection: 'EXPORT', freightType: bookingIndex % 2 === 0 ? 'CONTAINER' : 'BULK', cargoTypeId: cargoType?.id ?? null, cargoFreeText: cargoType ? null : `Export Djibouti interchange demo cargo ${trainIndex + 1}-${bookingIndex + 1}`, cargoTotalWeightVgm: weight, }), ); await inventoryRepo.save( inventoryRepo.create({ warehouseId: warehouse!.id, yardId: warehouseYard!.id, zoneId: warehouseZone!.id, bookingId: booking.id, quantity: 1, weight, status: 'DISPATCHED', inspectionStatus: 'PASSED', arrivedAt: new Date(departure.getTime() + 2 * 60 * 60 * 1000), inspectedAt: new Date(departure.getTime() + 3 * 60 * 60 * 1000), readyForLoadingAt: new Date(departure.getTime() + 4 * 60 * 60 * 1000), loadedAt: new Date(departure.getTime() + 4.5 * 60 * 60 * 1000), dispatchedAt: departure, notes: '[ICD-DEMO] Eligible for Djibouti export unload and interchange generation', }), ); await scheduleBookingRepo.save( scheduleBookingRepo.create({ trainScheduleId: schedule.id, bookingId: booking.id, }), ); } seededSchedules.push(schedule); } console.log('Export Djibouti interchange demo seeded.'); for (const schedule of seededSchedules) { console.log(`Train number: ${schedule.trainNumber}`); console.log(`Schedule ID: ${schedule.id}`); } console.log('Open Djibouti Unloading, click "Auto Unload Export Items", then check Interchange Documents.'); } finally { await app.close(); } } main().catch((error) => { console.error('Export Djibouti interchange demo seed failed:', error); process.exit(1); });