feat(train-scheduling): notify customer of CAS on direct-to-train load

Direct truck-to-train export cargo skips the warehouse, so the existing
carriage acceptance sheet ready notice (fired on warehouse receive)
never reached these bookings. Their handover moment is the load itself.

Extract notifyCarriageAcceptanceReady into a shared notifications util
(was private to WarehouseInventoryService) and call it from
BookingJourneyService.loadBooking for EXPORT + DIRECT_TO_TRAIN bookings,
right after the GRN gate, before the load transaction proceeds.
This commit is contained in:
Hagernesh
2026-08-13 09:27:01 +00:00
parent ae6797ea05
commit 89dba01cd7
4 changed files with 75 additions and 22 deletions

View File

@@ -28,7 +28,10 @@ import type { InterchangeDocument } from '../interchange-documents/entities/inte
import { LastMileService } from '../last-mile/last-mile.service';
import { UpdateLastMileDto } from '../last-mile/dto/update-last-mile.dto';
import { NotificationsService } from '../notifications/notifications.service';
import { sendCompanyChannels } from '../notifications/notify-company.util';
import {
sendCompanyChannels,
notifyCarriageAcceptanceReady as notifyCarriageAcceptanceReadyShared,
} from '../notifications/notify-company.util';
import {
companyNotifyPhoneExpr,
primaryContactUserJoin,
@@ -6167,26 +6170,13 @@ export class WarehouseInventoryService {
* fires right after receive, not at marshalling.
*/
private async notifyCarriageAcceptanceReady(bookingId: string): Promise<void> {
try {
const [b]: Array<{ companyId: string | null; reference: string }> = await this.dataSource.query(
`SELECT company_id AS "companyId", reference FROM freight.bookings WHERE id = $1 AND deleted_at IS NULL`,
[bookingId],
);
if (!b?.companyId) return;
const body = `Your carriage acceptance sheet for booking ${b.reference} is ready to download from the portal.`;
await this.inbox.notify({
recipients: { companyId: b.companyId },
audience: NotificationAudience.PORTAL,
type: NotificationType.DOCUMENT_ACTION,
title: 'Carriage acceptance sheet ready',
body,
link: `/bookings/${bookingId}`,
data: { bookingId, reference: b.reference },
});
await sendCompanyChannels(this.dataSource, this.notifications, b.companyId, body);
} catch (err) {
this.logger.warn(`Carriage acceptance ready notify failed for ${bookingId}: ${(err as Error).message}`);
}
await notifyCarriageAcceptanceReadyShared(
this.dataSource,
this.notifications,
this.inbox,
bookingId,
this.logger,
);
}
private async notifyOwnerInventoryReceived(params: {