import 'reflect-metadata'; import { config } from 'dotenv'; import { resolve } from 'path'; import { NestFactory } from '@nestjs/core'; import { DataSource } from 'typeorm'; config({ path: resolve(__dirname, '../../.env') }); process.env.TYPEORM_LOGGING = 'false'; import { AppModule } from '../app.module'; import { WarehouseInventoryService } from '../modules/warehouses/warehouse-inventory.service'; /** * One-off backfill for bookings caught by the autoArriveAtFinalYard bug * (fixed in booking-journey.service.ts): the bulk final-yard arrival used to * flip booking status to ARRIVED/COMPLETED without ever emitting * booking.unloadedAtYard, so WarehouseInventoryService never created their * warehouse_inventory row. Reuses the same idempotent listener the live * event now calls, so it's safe to re-run. */ async function main() { const app = await NestFactory.createApplicationContext(AppModule, { logger: ['error', 'warn'], }); try { const dataSource = app.get(DataSource); const inventory = app.get(WarehouseInventoryService); // Skip bookings destined for a pure facility yard (has a facility but no // warehouse, e.g. Dire Dawa) — those are fully represented by their // facility_handling_events UNLOAD record, not a warehouse_inventory row. // Same gate as BookingJourneyService.autoArriveAtFinalYard. const bookings: { id: string; tradeDirection: string }[] = await dataSource.query( `SELECT b.id, b.trade_direction AS "tradeDirection" FROM freight.bookings b JOIN freight.yards dy ON dy.id = b.destination_yard_id LEFT JOIN freight.yard_facilities yf ON yf.yard_id = dy.id AND yf.deleted_at IS NULL AND yf.is_active = true WHERE b.deleted_at IS NULL AND b.trade_direction IN ('IMPORT', 'DOMESTIC') AND b.status IN ('ARRIVED', 'COMPLETED') AND NOT (dy.has_facility = true AND COALESCE(yf.has_warehouse, false) = false) AND NOT EXISTS ( SELECT 1 FROM freight.warehouse_inventory wi WHERE wi.booking_id = b.id AND wi.deleted_at IS NULL )`, ); if (bookings.length === 0) { console.log('No bookings missing their unload inventory row.'); return; } console.log(`Backfilling ${bookings.length} booking(s)...`); for (const booking of bookings) { await inventory.handleBookingUnloadedAtYard({ bookingId: booking.id, tradeDirection: booking.tradeDirection, }); console.log(` - ${booking.id} (${booking.tradeDirection})`); } } finally { await app.close(); } } main().catch((error) => { console.error(error); process.exitCode = 1; });