mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 12:30:58 +00:00
Production cleanups
This commit is contained in:
@@ -17,6 +17,7 @@ function generateRef(): string {
|
||||
return Array.from({ length: 6 }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* For package round-trip bookings, totalMinor in the DB may have been stored as a
|
||||
* single-leg amount before the server fix. Recompute from the tier price when needed.
|
||||
|
||||
@@ -14,7 +14,7 @@ const BOOKING_CUTOFF_MS = 30 * 60 * 1000;
|
||||
|
||||
function generateRef(): string {
|
||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
return 'EDR-' + Array.from({ length: 6 }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
|
||||
return Array.from({ length: 6 }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
|
||||
}
|
||||
|
||||
// Ethiopian mobile prefixes: Ethio Telecom (09xx) and Safaricom ET (07xx)
|
||||
|
||||
@@ -17,7 +17,7 @@ class UpsertBaggageAllowanceDto {
|
||||
}
|
||||
|
||||
// ── IAM-protected agent/supervisor routes ────────────────────────────────────
|
||||
@ApiTags('Excess Baggage')
|
||||
@ApiTags('Excess Luggage')
|
||||
@Controller('agents/excess-baggage')
|
||||
@UseGuards(IamJwtGuard)
|
||||
@ApiBearerAuth('IAM-auth')
|
||||
@@ -100,7 +100,7 @@ export class ExcessBaggageAgentController {
|
||||
}
|
||||
|
||||
// ── Public pay-by-token routes (passenger self-service) ──────────────────────
|
||||
@ApiTags('Excess Baggage')
|
||||
@ApiTags('Excess Luggage')
|
||||
@Controller('excess-baggage')
|
||||
export class ExcessBaggagePublicController {
|
||||
constructor(private service: ExcessBaggageService) {}
|
||||
|
||||
@@ -4,9 +4,10 @@ import { SearchService } from './search.service';
|
||||
import { CurrencyModule } from '../currency/currency.module';
|
||||
import { FareEngineModule } from '../fare-engine/fare-engine.module';
|
||||
import { SegmentsModule } from '../segments/segments.module';
|
||||
import { SystemConfigModule } from '../system-config/system-config.module';
|
||||
|
||||
@Module({
|
||||
imports: [CurrencyModule, FareEngineModule, SegmentsModule],
|
||||
imports: [CurrencyModule, FareEngineModule, SegmentsModule, SystemConfigModule],
|
||||
controllers: [SearchController],
|
||||
providers: [SearchService],
|
||||
exports: [SearchService],
|
||||
|
||||
@@ -6,6 +6,7 @@ import { FareEngineService } from '../fare-engine/fare-engine.service';
|
||||
import { SegmentsService } from '../segments/segments.service';
|
||||
import { resolveCurrencyFromNationality } from '../fare-engine/fare-engine.dto';
|
||||
import { Currency } from '@prisma/client';
|
||||
import { SystemConfigService, CONFIG_KEYS } from '../system-config/system-config.service';
|
||||
|
||||
const POINTS_TO_MINOR = 10;
|
||||
|
||||
@@ -40,8 +41,13 @@ export class SearchService {
|
||||
private currencyService: CurrencyService,
|
||||
private fareEngine: FareEngineService,
|
||||
private segmentsService: SegmentsService,
|
||||
private systemConfig: SystemConfigService,
|
||||
) {}
|
||||
|
||||
private async getCutoffHours(): Promise<number> {
|
||||
return this.systemConfig.getNumber(CONFIG_KEYS.HOLD_CUTOFF_HOURS_BEFORE_DEPARTURE);
|
||||
}
|
||||
|
||||
async searchTrips(dto: SearchTripsDto) {
|
||||
const [direct, transit] = await Promise.all([
|
||||
this.searchSchedules(
|
||||
@@ -166,13 +172,14 @@ export class SearchService {
|
||||
|
||||
const schedules = await this.prisma.trainSchedule.findMany({
|
||||
where: {
|
||||
status: { in: ['SCHEDULED', 'BOARDING'] },
|
||||
status: 'SCHEDULED',
|
||||
isPackageOnly: false,
|
||||
OR: [
|
||||
{ departureAt: { gte: windowStart, lt: requestedDate } },
|
||||
{ departureAt: { gte: requestedNextDay < now ? now : requestedNextDay, lt: windowEnd } },
|
||||
],
|
||||
stopTimes: { some: { stationId: originStationId } },
|
||||
coachAssignments: { some: {} },
|
||||
},
|
||||
include: SCHEDULE_INCLUDE,
|
||||
orderBy: { departureAt: 'asc' },
|
||||
@@ -183,7 +190,7 @@ export class SearchService {
|
||||
this.buildScheduleResult(schedule as any, originStationId, destinationStationId, totalPassengers, nationality)
|
||||
)
|
||||
);
|
||||
return results.filter(Boolean);
|
||||
return results.filter((r): r is NonNullable<typeof r> => !!r && r.hasAvailability);
|
||||
}
|
||||
|
||||
private async searchSchedules(
|
||||
@@ -200,12 +207,17 @@ export class SearchService {
|
||||
const now = new Date();
|
||||
const totalPassengers = adultCount + (childCount ?? 0);
|
||||
|
||||
const cutoffHours = await this.getCutoffHours();
|
||||
const cutoffThreshold = new Date(now.getTime() + cutoffHours * 60 * 60 * 1000);
|
||||
const earliest = new Date(Math.max((date < now ? now : date).getTime(), cutoffThreshold.getTime()));
|
||||
|
||||
const schedules = await this.prisma.trainSchedule.findMany({
|
||||
where: {
|
||||
status: { in: ['SCHEDULED', 'BOARDING'] },
|
||||
status: 'SCHEDULED',
|
||||
isPackageOnly: false,
|
||||
departureAt: { gte: date < now ? now : date, lt: nextDay },
|
||||
departureAt: { gte: earliest, lt: nextDay },
|
||||
stopTimes: { some: { stationId: originStationId } },
|
||||
coachAssignments: { some: {} },
|
||||
},
|
||||
include: SCHEDULE_INCLUDE,
|
||||
});
|
||||
@@ -215,7 +227,7 @@ export class SearchService {
|
||||
this.buildScheduleResult(schedule as any, originStationId, destinationStationId, totalPassengers, nationality)
|
||||
)
|
||||
);
|
||||
return results.filter(Boolean);
|
||||
return results.filter((r): r is NonNullable<typeof r> => !!r && r.hasAvailability);
|
||||
}
|
||||
|
||||
// ── Transit search ─────────────────────────────────────────────────────────
|
||||
@@ -241,26 +253,31 @@ export class SearchService {
|
||||
const [leg1Schedules, allCandidates] = await Promise.all([
|
||||
this.prisma.trainSchedule.findMany({
|
||||
where: {
|
||||
status: { in: ['SCHEDULED', 'BOARDING'] },
|
||||
status: 'SCHEDULED',
|
||||
isPackageOnly: false,
|
||||
departureAt: { gte: dayStart, lt: dayEnd },
|
||||
stopTimes: { some: { stationId: originStationId } },
|
||||
coachAssignments: { some: {} },
|
||||
},
|
||||
include: SCHEDULE_INCLUDE,
|
||||
}),
|
||||
this.prisma.trainSchedule.findMany({
|
||||
where: {
|
||||
status: { in: ['SCHEDULED', 'BOARDING'] },
|
||||
status: 'SCHEDULED',
|
||||
isPackageOnly: false,
|
||||
departureAt: { gte: dayStart, lt: leg2WindowEnd },
|
||||
coachAssignments: { some: {} },
|
||||
},
|
||||
include: SCHEDULE_INCLUDE,
|
||||
}),
|
||||
]);
|
||||
|
||||
const cutoffHours = await this.getCutoffHours();
|
||||
const cutoffThreshold = new Date(Date.now() + cutoffHours * 60 * 60 * 1000);
|
||||
|
||||
const results: any[] = [];
|
||||
|
||||
for (const leg1 of leg1Schedules as ScheduleWithIncludes[]) {
|
||||
for (const leg1 of (leg1Schedules as ScheduleWithIncludes[]).filter(s => new Date(s.departureAt) > cutoffThreshold)) {
|
||||
const originStop = leg1.stopTimes.find(s => s.stationId === originStationId);
|
||||
if (!originStop) continue;
|
||||
|
||||
@@ -354,6 +371,9 @@ export class SearchService {
|
||||
.map((s: any) => s.id as string)
|
||||
);
|
||||
|
||||
// Exclude schedules with no seats at all
|
||||
if (allValidSeatIds.length === 0) return null;
|
||||
|
||||
// Run availability batch and fare calculation in parallel
|
||||
const [freeSeats, faresByClass] = await Promise.all([
|
||||
this.segmentsService.getFreeSeatIds(
|
||||
|
||||
Reference in New Issue
Block a user