fix intercity unloading at facility

This commit is contained in:
Hagernesh
2026-08-01 09:10:04 +00:00
parent c3ba0192a9
commit 035369af43
2 changed files with 41 additions and 4 deletions

View File

@@ -327,19 +327,48 @@ export class BookingJourneyService {
RETURNING b.id, b.trade_direction`,
[schedule.id, schedule.destinationStationId, now],
);
if (rows.length === 0) return [];
// The facility took the cargo off the train at the final yard — raise its
// GRN, same as the per-booking unloadBooking() path does. Only when that
// yard also has a warehouse (or has no facility at all, e.g. Kality) does
// WarehouseInventoryService additionally get to allocate a warehouse/yard/
// zone row: a pure facility yard (Dire Dawa, Modjo, Sebeta, Adama) is
// fully represented by the facility event alone — there is nothing there
// for warehouse_inventory's NOT NULL warehouse/yard/zone to point at.
const facility = await this.yardFacilities.facilityForYard(schedule.destinationStationId);
const bookings = await manager
.getRepository(Booking)
.find({ where: { id: In(rows.map((r) => r.id)) }, relations: ['company'] });
const bookingById = new Map(bookings.map((b) => [b.id, b]));
for (const row of rows) {
// Intercity rows just completed — let a ONE_TIME contract close on delivery.
if (row.trade_direction === 'DOMESTIC') {
this.events.emit('booking.completed', { bookingId: row.id });
}
const booking = bookingById.get(row.id);
if (booking) {
await this.facilityHandling.recordHandling(manager, {
booking,
yardId: schedule.destinationStationId,
trainScheduleId: schedule.id,
eventType: 'UNLOAD',
occurredAt: now,
});
}
// Same event the per-booking unloadBooking() path emits — WarehouseInventoryService
// listens for this to auto-create the warehouse_inventory row (import/intercity only,
// it filters EXPORT itself). The bulk SQL update above skipped this entirely, so
// bookings caught by this fallback never left "awaiting unload".
this.events.emit('booking.unloadedAtYard', {
bookingId: row.id,
tradeDirection: row.trade_direction,
});
if (row.trade_direction !== 'EXPORT' && (!facility?.hasFacility || facility.hasWarehouse)) {
this.events.emit('booking.unloadedAtYard', {
bookingId: row.id,
tradeDirection: row.trade_direction,
});
}
}
return rows.map((r) => r.id);
}

View File

@@ -27,12 +27,20 @@ async function main() {
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