From fa5fde6b6bcc96b17eab320b69a08ba640a40d29 Mon Sep 17 00:00:00 2001 From: Hagernesh Date: Thu, 9 Jul 2026 08:59:58 +0000 Subject: [PATCH] Fix slow GRN button and sms timeout --- .../strategies/notification.sms.strategy.ts | 5 ++ .../warehouses/warehouse-inventory.service.ts | 47 +++++++++++++++---- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/apps/edr-freight-api/src/modules/notifications/strategies/notification.sms.strategy.ts b/apps/edr-freight-api/src/modules/notifications/strategies/notification.sms.strategy.ts index d8404e84c..cddc67b2d 100644 --- a/apps/edr-freight-api/src/modules/notifications/strategies/notification.sms.strategy.ts +++ b/apps/edr-freight-api/src/modules/notifications/strategies/notification.sms.strategy.ts @@ -22,6 +22,10 @@ export class SmsNotificationStrategy implements NotificationStrategy { this.logger.debug(`Sending SMS to ${recipient} via ${url}`); + // axios defaults to no timeout — a hanging gateway would block the caller + // (and any transaction it sits in) indefinitely. Always bound the wait. + const timeout = Number(this.configService.get("SMS_TIMEOUT_MS") ?? 8000); + try { const response = await axios.post( url, @@ -34,6 +38,7 @@ export class SmsNotificationStrategy implements NotificationStrategy { callbackUrl: "", }, { + timeout, headers: { accept: "*/*", "Content-Type": "application/json", 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 9b2f53a99..06e84d455 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 @@ -860,6 +860,25 @@ export class WarehouseInventoryService { /** Bulk-receive eligible PAID bookings into a location. Skips duplicates / wrong direction. */ async bulkReceive(dto: BulkReceiveDto): Promise { const result: BulkReceiveResult = { receivedCount: 0, skippedCount: 0, results: [] }; + /** Sent after the transaction commits so the gateway never blocks the receive. */ + const pendingNotifications: Array<{ + owner: { + phone?: string | null; + ownerName?: string | null; + bookingReference?: string | null; + grnNumber: string; + direction?: string | null; + warehouseId?: string | null; + }; + booking: { + companyId?: string | null; + reference?: string | null; + hasFirstMile?: boolean; + hasLastMile?: boolean; + customerTruckAssignedAt?: string | null; + }; + bookingId: string; + }> = []; await this.dataSource.transaction(async (manager) => { await this.validateLocation(manager, { @@ -1032,21 +1051,33 @@ export class WarehouseInventoryService { manager, ); - await this.notifyOwnerInventoryReceived({ - phone: truckEntrance?.customerPhone ?? booking.customerPhone, - ownerName: truckEntrance?.ownerName ?? booking.customer, - bookingReference: truckEntrance?.edrDigitalBookingId ?? booking.reference, - grnNumber, - direction: dto.direction, - warehouseId: dto.warehouseId, + // Queued, not sent here: an SMS/email round-trip inside the transaction + // holds capacity/location locks open for the whole gateway latency. + pendingNotifications.push({ + owner: { + phone: truckEntrance?.customerPhone ?? booking.customerPhone, + ownerName: truckEntrance?.ownerName ?? booking.customer, + bookingReference: truckEntrance?.edrDigitalBookingId ?? booking.reference, + grnNumber, + direction: dto.direction, + warehouseId: dto.warehouseId, + }, + booking, + bookingId, }); result.receivedCount += 1; result.results.push({ bookingId, status: 'RECEIVED', inventoryId: saved.id, grnNumber }); - void this.notifyTruckAssignmentNeeded(booking, bookingId); } }); + // Fan out after commit, un-awaited: the receive response must not wait on the + // SMS gateway. Both notifiers swallow their own errors. + for (const pending of pendingNotifications) { + void this.notifyOwnerInventoryReceived(pending.owner); + void this.notifyTruckAssignmentNeeded(pending.booking, pending.bookingId); + } + return result; }