enhance booking and contract notification systems

- Added detailed logging for socket connection events in useBookingWindowSocket.
- Introduced new notification types for contract status and schedule updates.
- Updated notification visuals to include new icons for contract status.
- Enhanced notification href resolution for contract status and schedule updates.
- Implemented booking lifecycle notifier service for customer and staff notifications.
- Created contract notifier service for managing contract lifecycle notifications.
- Added end-to-end tests for booking window socket functionality.
This commit is contained in:
Marshal
2026-07-06 21:03:08 +00:00
parent 8bd00ea78a
commit 05b13e84a8
38 changed files with 1450 additions and 95 deletions

View File

@@ -1517,6 +1517,12 @@ export class BookingBatchService implements OnModuleInit {
"PREPAID",
);
await this.notifier.payNow(booking, deadline);
// Customer tracking: a wagon slot is reserved and the freight pay window is
// open. Doc-trigger path — silent no-op for bookings without milestone rows.
void this.completeTrackingMilestones(booking.id, [
"WAGON_REQUESTED",
"FREIGHT_PAYMENT_PENDING",
]);
}
/** Allocate a booking to the schedule's train (creates the TrainScheduleBooking link). */
@@ -1548,6 +1554,15 @@ export class BookingBatchService implements OnModuleInit {
this.notifier.secured(booking, reason);
void this.triggerWagonAllocation(scheduleId);
void this.markWagonAllocatedMilestone(booking.id);
// Customer tracking: freight payment settled (commercial pay-window path).
// Government allocations don't pay upfront — theirs stay pending.
if (reason === 'paid') {
void this.completeTrackingMilestones(booking.id, [
'WAGON_REQUESTED',
'FREIGHT_PAYMENT_PENDING',
'FREIGHT_PAYMENT_SETTLED',
]);
}
}
private async markWagonAllocatedMilestone(bookingId: string): Promise<void> {
@@ -1559,6 +1574,27 @@ export class BookingBatchService implements OnModuleInit {
}
}
/**
* Complete customer-tracking milestones on lifecycle events via the
* doc-trigger path — a silent no-op for bookings without milestone rows
* (non-customs bookings). Never blocks the batch action.
*/
private async completeTrackingMilestones(
bookingId: string,
codes: string[],
): Promise<void> {
if (!this.milestoneService) return;
for (const code of codes) {
try {
await this.milestoneService.completeByDocTrigger({ bookingId }, code);
} catch (err) {
this.logger.warn(
`Milestone ${code} completion failed for booking ${bookingId}: ${(err as Error).message}`,
);
}
}
}
/**
* Expire an unpaid reservation and free its capacity. With day-level pooling we
* also clear `trainScheduleId` so the booking is no longer pinned to the train