Cut off time configuration

This commit is contained in:
Muluhabt
2026-07-24 09:01:27 +03:00
parent 64e1130f7d
commit 7320c359d1
10 changed files with 618 additions and 207 deletions

View File

@@ -5,6 +5,7 @@ import { PrismaService } from '../../common/prisma.service';
import { NotificationsService } from '../notifications/notifications.service';
import { SystemConfigService, CONFIG_KEYS } from '../system-config/system-config.service';
import { AuditService } from '../../common/audit.service';
import { resolveBookingSegment } from '../../common/utils/segment-resolver.utils';
import * as QRCode from 'qrcode';
interface OfflineValidation {
@@ -129,6 +130,7 @@ export class TicketsService {
: { fullName: 'Guest', email: guestEmail, phone: guestPhone };
const segment = resolveBookingSegment(t.booking?.schedule, t.booking?.originStationId, t.booking?.destinationStationId);
return {
id: t.id,
ticketNumber: t.barcodePayload,
@@ -152,20 +154,14 @@ export class TicketsService {
contactPhone: t.booking?.contactPhone,
returnSchedule: t.booking?.returnSchedule ?? null,
seats: t.booking?.seats ?? [],
originStation: (() => {
const id = t.booking?.originStationId;
if (!id) return t.booking?.schedule?.originStation ?? null;
const stop = t.booking?.schedule?.stopTimes?.find((st: any) => st.stationId === id);
return stop?.station ?? t.booking?.schedule?.originStation ?? null;
})(),
destinationStation: (() => {
const id = t.booking?.destinationStationId;
if (!id) return t.booking?.schedule?.destinationStation ?? null;
const stop = t.booking?.schedule?.stopTimes?.find((st: any) => st.stationId === id);
return stop?.station ?? t.booking?.schedule?.destinationStation ?? null;
})(),
originStation: segment.origin,
destinationStation: segment.destination,
},
schedule: t.booking?.schedule,
schedule: t.booking?.schedule ? {
...t.booking.schedule,
departureAt: segment.departureAt,
arrivalAt: segment.arrivalAt,
} : null,
seat: t.seat ? {
id: t.seat.id,
seatNumber: t.seat.seatNumber,
@@ -643,11 +639,14 @@ export class TicketsService {
throw new NotFoundException('No ticket found for this booking');
}
// Check if ticket date matches today
// Check if ticket date matches today. Boarding window is relative to the
// passenger's actual boarding stop, not the train's origin — for a mid-route
// boarding these differ.
const today = new Date();
if ((booking as any).schedule?.departureAt) {
const departureTime = new Date((booking as any).schedule.departureAt);
const boardingSegment = resolveBookingSegment((booking as any).schedule, (booking as any).originStationId, (booking as any).destinationStationId);
if (boardingSegment.departureAt) {
const departureTime = new Date(boardingSegment.departureAt);
const boardingWindowHours = await this.systemConfig.getNumber(CONFIG_KEYS.BOARDING_WINDOW_HOURS_BEFORE_DEPARTURE);
const boardingOpenTime = new Date(departureTime.getTime() - boardingWindowHours * 60 * 60 * 1000);
@@ -673,18 +672,6 @@ export class TicketsService {
// Send notifications after successful boarding
await this.sendBoardingNotifications(booking, ticket, result.leg || 'OUTBOUND');
// Resolve user-selected segment rather than the full schedule route
const _schedStops = (booking as any).schedule?.stopTimes ?? [];
const _resolveStation = (id: string | null | undefined, fallback: any) => {
if (id) {
const found = _schedStops.find((st: any) => st.stationId === id)?.station;
if (found) return found;
}
return fallback;
};
const boardingOrigin = _resolveStation((booking as any).originStationId, (booking as any).schedule?.originStation);
const boardingDest = _resolveStation((booking as any).destinationStationId, (booking as any).schedule?.destinationStation);
return {
success: true,
message: `Passenger boarded successfully (${result.leg || 'OUTBOUND'} leg)`,
@@ -693,11 +680,11 @@ export class TicketsService {
ticketNumber: ticket.barcodePayload,
bookingRef: booking.bookingRef,
passengerName: seatInfo?.passengerName || ticket.passengerName || 'N/A',
route: `${boardingOrigin?.name || 'N/A'}${boardingDest?.name || 'N/A'}`,
route: `${boardingSegment.origin?.name || 'N/A'}${boardingSegment.destination?.name || 'N/A'}`,
seat: seatNumber,
coach: coachNumber,
trainName: (booking as any).schedule?.train?.name || (booking as any).schedule?.train?.number || 'N/A',
departureTime: (booking as any).schedule?.departureAt,
departureTime: boardingSegment.departureAt,
boardedAt: result.validatedAt,
leg: result.leg || 'OUTBOUND',
bookingType: booking.bookingType,