diff --git a/apps/edr-freight-api/src/modules/warehouses/warehouse-inventory.service.ts b/apps/edr-freight-api/src/modules/warehouses/warehouse-inventory.service.ts index 07c720cff..70abf635b 100644 --- a/apps/edr-freight-api/src/modules/warehouses/warehouse-inventory.service.ts +++ b/apps/edr-freight-api/src/modules/warehouses/warehouse-inventory.service.ts @@ -1542,11 +1542,38 @@ export class WarehouseInventoryService { // their already-allocated wagons. Reuses the single-item load() machinery. /** Pre-dispatch EXPORT trains that have inventory waiting to be (or already) loaded. */ + /** + * The bookings riding a train schedule. + * + * Export flow: booked -> paid -> received at the warehouse (first-mile or + * self-haul) -> GRN -> loaded onto the wagons allocated to it. A booking + * actually reaches a train through WAGON ALLOCATION + * (train_schedules -> train_sets -> train_set_wagons -> wagon_booking_allocations), + * which is what the allocation UI writes. `train_schedule_bookings` is only + * ever written by the demo seeders — keying off it alone left this queue + * permanently empty for real traffic — so both sources are unioned. + */ + private readonly SCHEDULE_BOOKINGS_CTE = ` + sched_bookings AS ( + SELECT ts.id AS schedule_id, wba.booking_id + FROM freight.train_schedules ts + JOIN freight.train_set_wagons tsw + ON tsw.train_set_id = ts.train_set_id AND tsw.deleted_at IS NULL + JOIN freight.wagon_booking_allocations wba + ON wba.train_set_wagon_id = tsw.id AND wba.deleted_at IS NULL + WHERE ts.deleted_at IS NULL + UNION + SELECT tsb.train_schedule_id, tsb.booking_id + FROM freight.train_schedule_bookings tsb + WHERE tsb.deleted_at IS NULL + )`; + async loadableTrains(): Promise { const rows: Array< LoadableTrainRow & { originCountry: string | null; destinationCountry: string | null } > = await this.dataSource.query( - `SELECT ts.id AS "scheduleId", + `WITH ${this.SCHEDULE_BOOKINGS_CTE} + SELECT ts.id AS "scheduleId", ts.train_number AS "trainNumber", oy.code AS "origin", dy.code AS "destination", @@ -1554,15 +1581,15 @@ export class WarehouseInventoryService { dy.country AS "destinationCountry", ts.status AS "status", ts.scheduled_departure_date AS "departureTime", - (SELECT count(*) FROM freight.train_schedule_bookings tsb + (SELECT count(*) FROM sched_bookings sb JOIN freight.warehouse_inventory inv - ON inv.booking_id = tsb.booking_id AND inv.deleted_at IS NULL - WHERE tsb.train_schedule_id = ts.id AND tsb.deleted_at IS NULL + ON inv.booking_id = sb.booking_id AND inv.deleted_at IS NULL + WHERE sb.schedule_id = ts.id AND inv.status IN ('RECEIVED','STORED','RESERVED','READY_FOR_LOADING')) AS "readyCount", - (SELECT count(*) FROM freight.train_schedule_bookings tsb + (SELECT count(*) FROM sched_bookings sb JOIN freight.warehouse_inventory inv - ON inv.booking_id = tsb.booking_id AND inv.deleted_at IS NULL - WHERE tsb.train_schedule_id = ts.id AND tsb.deleted_at IS NULL + ON inv.booking_id = sb.booking_id AND inv.deleted_at IS NULL + WHERE sb.schedule_id = ts.id AND inv.status = 'LOADED') AS "loadedCount" FROM freight.train_schedules ts LEFT JOIN freight.yards oy ON oy.id = ts.origin_station_id @@ -1570,10 +1597,10 @@ export class WarehouseInventoryService { WHERE ts.deleted_at IS NULL AND ts.status = ANY($1) AND EXISTS ( - SELECT 1 FROM freight.train_schedule_bookings tsb2 + SELECT 1 FROM sched_bookings sb2 JOIN freight.warehouse_inventory inv2 - ON inv2.booking_id = tsb2.booking_id AND inv2.deleted_at IS NULL - WHERE tsb2.train_schedule_id = ts.id AND tsb2.deleted_at IS NULL + ON inv2.booking_id = sb2.booking_id AND inv2.deleted_at IS NULL + WHERE sb2.schedule_id = ts.id AND inv2.status IN ('RECEIVED','STORED','RESERVED','READY_FOR_LOADING','LOADED') ) ORDER BY ts.scheduled_departure_date ASC NULLS LAST`, @@ -1599,7 +1626,8 @@ export class WarehouseInventoryService { */ async trainLoadableItems(scheduleId: string): Promise { const rows: Array> = await this.dataSource.query( - `SELECT inv.id AS "id", + `WITH ${this.SCHEDULE_BOOKINGS_CTE} + SELECT inv.id AS "id", inv.booking_id AS "bookingId", b.reference AS "bookingReference", company.name AS "customerName", @@ -1612,9 +1640,9 @@ export class WarehouseInventoryService { wl.wagon_id AS "wagonId", wl.wagon_number AS "wagonNumber", wl.sequence_no AS "sequenceNo" - FROM freight.train_schedule_bookings tsb - JOIN freight.train_schedules ts ON ts.id = tsb.train_schedule_id - JOIN freight.bookings b ON b.id = tsb.booking_id AND b.deleted_at IS NULL + FROM sched_bookings sb + JOIN freight.train_schedules ts ON ts.id = sb.schedule_id + JOIN freight.bookings b ON b.id = sb.booking_id AND b.deleted_at IS NULL JOIN freight.warehouse_inventory inv ON inv.booking_id = b.id AND inv.deleted_at IS NULL LEFT JOIN freight.companies company ON company.id = b.company_id LEFT JOIN freight.cargo_types cgt ON cgt.id = b.cargo_type_id @@ -1631,7 +1659,7 @@ export class WarehouseInventoryService { ORDER BY tsw.sequence_no ASC NULLS LAST LIMIT 1 ) wl ON true - WHERE tsb.train_schedule_id = $1 AND tsb.deleted_at IS NULL + WHERE sb.schedule_id = $1 AND inv.status IN ('RECEIVED','STORED','RESERVED','READY_FOR_LOADING','LOADED') ORDER BY wl.sequence_no ASC NULLS LAST, b.reference ASC NULLS LAST, ct.container_number ASC NULLS LAST`, [scheduleId],