fix(warehouse): scope the arrival GRN stamp to EXPORT only

The previous commit made autoUnloadArrived and unloadBooking raise a GRN for any
direction, which changed import behaviour. Import keeps its own GRN handling
(autoUnloadArrivedBookings) and is left exactly as it was. Both paths now stamp a
GRN only when the booking is EXPORT — the direction whose cargo needs one to be
loaded onto a train. Import and domestic behave as before.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hagernesh
2026-07-16 13:45:50 +00:00
parent a95667fde4
commit fc636fbe4f

View File

@@ -1032,8 +1032,8 @@ export class WarehouseInventoryService {
result.results.push({ bookingId: booking.id, status: 'FAILED', reason: 'No warehouse/yard/zone configured' }); result.results.push({ bookingId: booking.id, status: 'FAILED', reason: 'No warehouse/yard/zone configured' });
continue; continue;
} }
// Goods reaching the warehouse always get a GRN, whichever path brought // EXPORT goods get their GRN on arrival at the warehouse — nothing loads
// them in — nothing loads onto a train without one. // onto a train without one. Import GRN handling is left untouched.
const saved = await this.inventoryRepository.create({ const saved = await this.inventoryRepository.create({
warehouseId: location.warehouseId, warehouseId: location.warehouseId,
yardId: location.yardId, yardId: location.yardId,
@@ -1043,11 +1043,9 @@ export class WarehouseInventoryService {
weight: Number(booking.weight) || 0, weight: Number(booking.weight) || 0,
status: 'RECEIVED', status: 'RECEIVED',
arrivedAt: new Date(), arrivedAt: new Date(),
grnNumber: this.generateGrnNumber( ...(booking.tradeDirection === 'EXPORT'
booking.tradeDirection ?? 'WH', ? { grnNumber: this.generateGrnNumber('EXPORT', booking.id, new Date()) }
booking.id, : {}),
new Date(),
),
notes: allocated?.rule ? `Auto-unloaded → ${allocated.path}` : 'Auto-unloaded from arrival queue', notes: allocated?.rule ? `Auto-unloaded → ${allocated.path}` : 'Auto-unloaded from arrival queue',
}); });
result.processedCount += 1; result.processedCount += 1;
@@ -1068,14 +1066,14 @@ export class WarehouseInventoryService {
/** Unload a single arrived booking into a chosen (or default) location. */ /** Unload a single arrived booking into a chosen (or default) location. */
async unloadBooking(bookingId: string, dto: UnloadBookingDto): Promise<WarehouseInventory> { async unloadBooking(bookingId: string, dto: UnloadBookingDto): Promise<WarehouseInventory> {
const existing = await this.inventoryRepository.findAll({ where: { bookingId } }); const existing = await this.inventoryRepository.findAll({ where: { bookingId } });
// Goods reaching the warehouse always get a GRN, whichever path brought them // EXPORT goods get their GRN on arrival at the warehouse — nothing loads onto
// in — nothing loads onto a train without one. // a train without one. Import GRN handling is left untouched.
const [bookingRow]: Array<{ tradeDirection: string | null }> = await this.dataSource.query( const [bookingRow]: Array<{ tradeDirection: string | null }> = await this.dataSource.query(
`SELECT trade_direction AS "tradeDirection" `SELECT trade_direction AS "tradeDirection"
FROM freight.bookings WHERE id = $1 AND deleted_at IS NULL`, FROM freight.bookings WHERE id = $1 AND deleted_at IS NULL`,
[bookingId], [bookingId],
); );
const grnDirection = bookingRow?.tradeDirection ?? 'WH'; const isExport = bookingRow?.tradeDirection === 'EXPORT';
let location: DefaultLocation | null = let location: DefaultLocation | null =
dto.warehouseId && dto.yardId && dto.zoneId dto.warehouseId && dto.yardId && dto.zoneId
@@ -1096,10 +1094,10 @@ export class WarehouseInventoryService {
zoneId: location.zoneId, zoneId: location.zoneId,
status: 'RECEIVED', status: 'RECEIVED',
arrivedAt, arrivedAt,
// Keep an already-issued GRN; only raise one if this row never got it. // Export only, and keep an already-issued GRN rather than reissuing.
...(existing[0].grnNumber ...(isExport && !existing[0].grnNumber
? {} ? { grnNumber: this.generateGrnNumber('EXPORT', bookingId, arrivedAt) }
: { grnNumber: this.generateGrnNumber(grnDirection, bookingId, arrivedAt) }), : {}),
notes: dto.notes ?? existing[0].notes ?? 'Unloaded', notes: dto.notes ?? existing[0].notes ?? 'Unloaded',
}); });
return this.findById(existing[0].id); return this.findById(existing[0].id);
@@ -1114,7 +1112,9 @@ export class WarehouseInventoryService {
weight: 0, weight: 0,
status: 'RECEIVED', status: 'RECEIVED',
arrivedAt, arrivedAt,
grnNumber: this.generateGrnNumber(grnDirection, bookingId, arrivedAt), ...(isExport
? { grnNumber: this.generateGrnNumber('EXPORT', bookingId, arrivedAt) }
: {}),
notes: dto.notes ?? 'Unloaded', notes: dto.notes ?? 'Unloaded',
}); });
return this.findById(saved.id); return this.findById(saved.id);