mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
fix(warehouse): raise a GRN on every warehouse receipt
Requiring a GRN before loading only works if every path into the warehouse issues one. Two did not: autoUnloadArrived and unloadBooking created RECEIVED inventory with a null grn_number, so cargo that genuinely arrived — by first mile or self haul — would have been stuck un-loadable behind the new gate. Both now stamp a GRN, derived from the booking's trade direction, matching receive/bulkReceive/autoUnloadArrivedBookings. unloadBooking keeps an already-issued GRN when it re-unloads an existing row rather than reissuing one. Every path that creates warehouse inventory now issues a GRN, so the chain is seamless: booking arrives (first mile or self haul) -> received -> GRN -> loadable onto its allocated wagon. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1032,6 +1032,8 @@ export class WarehouseInventoryService {
|
||||
result.results.push({ bookingId: booking.id, status: 'FAILED', reason: 'No warehouse/yard/zone configured' });
|
||||
continue;
|
||||
}
|
||||
// Goods reaching the warehouse always get a GRN, whichever path brought
|
||||
// them in — nothing loads onto a train without one.
|
||||
const saved = await this.inventoryRepository.create({
|
||||
warehouseId: location.warehouseId,
|
||||
yardId: location.yardId,
|
||||
@@ -1041,6 +1043,11 @@ export class WarehouseInventoryService {
|
||||
weight: Number(booking.weight) || 0,
|
||||
status: 'RECEIVED',
|
||||
arrivedAt: new Date(),
|
||||
grnNumber: this.generateGrnNumber(
|
||||
booking.tradeDirection ?? 'WH',
|
||||
booking.id,
|
||||
new Date(),
|
||||
),
|
||||
notes: allocated?.rule ? `Auto-unloaded → ${allocated.path}` : 'Auto-unloaded from arrival queue',
|
||||
});
|
||||
result.processedCount += 1;
|
||||
@@ -1061,6 +1068,14 @@ export class WarehouseInventoryService {
|
||||
/** Unload a single arrived booking into a chosen (or default) location. */
|
||||
async unloadBooking(bookingId: string, dto: UnloadBookingDto): Promise<WarehouseInventory> {
|
||||
const existing = await this.inventoryRepository.findAll({ where: { bookingId } });
|
||||
// Goods reaching the warehouse always get a GRN, whichever path brought them
|
||||
// in — nothing loads onto a train without one.
|
||||
const [bookingRow]: Array<{ tradeDirection: string | null }> = await this.dataSource.query(
|
||||
`SELECT trade_direction AS "tradeDirection"
|
||||
FROM freight.bookings WHERE id = $1 AND deleted_at IS NULL`,
|
||||
[bookingId],
|
||||
);
|
||||
const grnDirection = bookingRow?.tradeDirection ?? 'WH';
|
||||
|
||||
let location: DefaultLocation | null =
|
||||
dto.warehouseId && dto.yardId && dto.zoneId
|
||||
@@ -1081,6 +1096,10 @@ export class WarehouseInventoryService {
|
||||
zoneId: location.zoneId,
|
||||
status: 'RECEIVED',
|
||||
arrivedAt,
|
||||
// Keep an already-issued GRN; only raise one if this row never got it.
|
||||
...(existing[0].grnNumber
|
||||
? {}
|
||||
: { grnNumber: this.generateGrnNumber(grnDirection, bookingId, arrivedAt) }),
|
||||
notes: dto.notes ?? existing[0].notes ?? 'Unloaded',
|
||||
});
|
||||
return this.findById(existing[0].id);
|
||||
@@ -1095,6 +1114,7 @@ export class WarehouseInventoryService {
|
||||
weight: 0,
|
||||
status: 'RECEIVED',
|
||||
arrivedAt,
|
||||
grnNumber: this.generateGrnNumber(grnDirection, bookingId, arrivedAt),
|
||||
notes: dto.notes ?? 'Unloaded',
|
||||
});
|
||||
return this.findById(saved.id);
|
||||
|
||||
Reference in New Issue
Block a user