mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-08 21:48:18 +00:00
Package booking new rules update, cascade delete option updates
This commit is contained in:
@@ -51,9 +51,10 @@ Route stops carry distanceKm for fare-by-distance calculations.`,
|
||||
@UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth')
|
||||
@ApiOperation({ summary: 'Delete a route' })
|
||||
@ApiParam({ name: 'id', description: 'Route UUID' })
|
||||
@ApiQuery({ name: 'cascade', required: false, type: Boolean, description: 'Force delete with all related data' })
|
||||
@ApiResponse({ status: 200, description: 'Route deleted' })
|
||||
@ApiResponse({ status: 404, description: 'Route not found' })
|
||||
deleteRoute(@Param('id') id: string) { return this.service.deleteRoute(id); }
|
||||
deleteRoute(@Param('id') id: string, @Query('cascade') cascade?: string) { return this.service.deleteRoute(id, cascade === 'true'); }
|
||||
|
||||
// ── Route Stops ────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ export class RoutesService {
|
||||
});
|
||||
}
|
||||
|
||||
async deleteRoute(id: string) {
|
||||
async deleteRoute(id: string, cascade = false) {
|
||||
const route = await this.prisma.route.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
@@ -120,19 +120,77 @@ export class RoutesService {
|
||||
});
|
||||
if (!route) throw new NotFoundException('Route not found');
|
||||
|
||||
const constraints = [];
|
||||
if (route.schedules.length > 0) {
|
||||
constraints.push({
|
||||
entityName: 'schedule',
|
||||
count: route.schedules.length,
|
||||
action: 'delete' as const
|
||||
});
|
||||
if (!cascade) {
|
||||
const constraints = [];
|
||||
if (route.schedules.length > 0) {
|
||||
constraints.push({
|
||||
entityName: 'schedule',
|
||||
count: route.schedules.length,
|
||||
action: 'delete' as const
|
||||
});
|
||||
}
|
||||
|
||||
if (constraints.length > 0) {
|
||||
throw new DeleteOperationException('Route', `${route.code} (${route.name})`, constraints);
|
||||
}
|
||||
}
|
||||
|
||||
if (constraints.length > 0) {
|
||||
throw new DeleteOperationException('Route', `${route.code} (${route.name})`, constraints);
|
||||
if (cascade) {
|
||||
const scheduleIds = route.schedules.map(s => s.id);
|
||||
if (scheduleIds.length > 0) {
|
||||
await this.prisma.tripStopTime.deleteMany({ where: { scheduleId: { in: scheduleIds } } });
|
||||
await this.prisma.coachAssignment.deleteMany({ where: { scheduleId: { in: scheduleIds } } });
|
||||
await this.prisma.tripLiveStatus.deleteMany({ where: { scheduleId: { in: scheduleIds } } });
|
||||
await this.prisma.menuItem.deleteMany({ where: { scheduleId: { in: scheduleIds } } });
|
||||
await this.prisma.journeySegment.deleteMany({ where: { scheduleId: { in: scheduleIds } } });
|
||||
|
||||
const bookings = await this.prisma.booking.findMany({
|
||||
where: { OR: [{ scheduleId: { in: scheduleIds } }, { returnScheduleId: { in: scheduleIds } }] },
|
||||
select: { id: true },
|
||||
});
|
||||
if (bookings.length > 0) {
|
||||
const bookingIds = bookings.map(b => b.id);
|
||||
const tickets = await this.prisma.ticket.findMany({ where: { bookingId: { in: bookingIds } }, select: { id: true } });
|
||||
if (tickets.length > 0) {
|
||||
await this.prisma.gateValidationLog.deleteMany({ where: { ticketId: { in: tickets.map(t => t.id) } } });
|
||||
}
|
||||
await this.prisma.ticket.deleteMany({ where: { bookingId: { in: bookingIds } } });
|
||||
const foodOrders = await this.prisma.foodOrder.findMany({ where: { bookingId: { in: bookingIds } }, select: { id: true } });
|
||||
if (foodOrders.length > 0) {
|
||||
await this.prisma.foodOrderItem.deleteMany({ where: { orderId: { in: foodOrders.map(o => o.id) } } });
|
||||
}
|
||||
await this.prisma.foodOrder.deleteMany({ where: { bookingId: { in: bookingIds } } });
|
||||
const paymentIntents = await this.prisma.paymentIntent.findMany({ where: { bookingId: { in: bookingIds } }, select: { id: true } });
|
||||
if (paymentIntents.length > 0) {
|
||||
await this.prisma.paymentRefund.deleteMany({ where: { paymentIntentId: { in: paymentIntents.map(p => p.id) } } });
|
||||
}
|
||||
await this.prisma.paymentIntent.deleteMany({ where: { bookingId: { in: bookingIds } } });
|
||||
await this.prisma.bookingSeat.deleteMany({ where: { bookingId: { in: bookingIds } } });
|
||||
await this.prisma.agentBooking.deleteMany({ where: { bookingId: { in: bookingIds } } });
|
||||
await this.prisma.bookingModification.deleteMany({ where: { bookingId: { in: bookingIds } } });
|
||||
await this.prisma.bookingCancellation.deleteMany({ where: { bookingId: { in: bookingIds } } });
|
||||
await this.prisma.baggageBooking.deleteMany({ where: { bookingId: { in: bookingIds } } });
|
||||
await this.prisma.excessBaggageCharge.deleteMany({ where: { bookingId: { in: bookingIds } } });
|
||||
await this.prisma.journey.deleteMany({ where: { bookingId: { in: bookingIds } } });
|
||||
await this.prisma.booking.deleteMany({ where: { id: { in: bookingIds } } });
|
||||
}
|
||||
|
||||
const packages = await this.prisma.travelPackage.findMany({
|
||||
where: { OR: [{ outboundScheduleId: { in: scheduleIds } }, { returnScheduleId: { in: scheduleIds } }] },
|
||||
select: { id: true },
|
||||
});
|
||||
if (packages.length > 0) {
|
||||
const packageIds = packages.map(p => p.id);
|
||||
await this.prisma.packagePriceTier.deleteMany({ where: { packageId: { in: packageIds } } });
|
||||
await this.prisma.travelPackage.deleteMany({ where: { id: { in: packageIds } } });
|
||||
}
|
||||
|
||||
await this.prisma.trainSchedule.deleteMany({ where: { id: { in: scheduleIds } } });
|
||||
}
|
||||
await this.prisma.routeStop.deleteMany({ where: { routeId: id } });
|
||||
await this.prisma.routeCoachTemplate.deleteMany({ where: { routeId: id } });
|
||||
}
|
||||
|
||||
|
||||
await this.prisma.route.delete({ where: { id } });
|
||||
return { deleted: true, id };
|
||||
}
|
||||
|
||||
@@ -113,7 +113,8 @@ export class SchedulesController {
|
||||
@UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth')
|
||||
@ApiOperation({ summary: 'Delete a schedule' })
|
||||
@ApiParam({ name: 'id', description: 'TrainSchedule UUID' })
|
||||
deleteSchedule(@Param('id') id: string) { return this.service.deleteSchedule(id); }
|
||||
@ApiQuery({ name: 'cascade', required: false, type: Boolean })
|
||||
deleteSchedule(@Param('id') id: string, @Query('cascade') cascade?: string) { return this.service.deleteSchedule(id, cascade === 'true'); }
|
||||
|
||||
@Get(':id/stops')
|
||||
@IsPublic()
|
||||
|
||||
@@ -332,7 +332,7 @@ export class SchedulesService {
|
||||
return this.prisma.trainSchedule.update({ where: { id }, data: { status: dto.status } });
|
||||
}
|
||||
|
||||
async deleteSchedule(id: string) {
|
||||
async deleteSchedule(id: string, cascade = false) {
|
||||
const schedule = await this.prisma.trainSchedule.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
@@ -344,18 +344,20 @@ export class SchedulesService {
|
||||
});
|
||||
if (!schedule) throw new NotFoundException('Schedule not found');
|
||||
|
||||
const constraints = [];
|
||||
if ((schedule as any)._count.bookings > 0) {
|
||||
constraints.push({
|
||||
entityName: 'booking',
|
||||
count: (schedule as any)._count.bookings,
|
||||
action: 'cancel' as const
|
||||
});
|
||||
}
|
||||
|
||||
if (constraints.length > 0) {
|
||||
const scheduleName = `${schedule.train.number} (${schedule.originStation.name} → ${schedule.destinationStation.name})`;
|
||||
throw new DeleteOperationException('Schedule', scheduleName, constraints);
|
||||
if (!cascade) {
|
||||
const constraints = [];
|
||||
if ((schedule as any)._count.bookings > 0) {
|
||||
constraints.push({
|
||||
entityName: 'booking',
|
||||
count: (schedule as any)._count.bookings,
|
||||
action: 'cancel' as const
|
||||
});
|
||||
}
|
||||
|
||||
if (constraints.length > 0) {
|
||||
const scheduleName = `${schedule.train.number} (${schedule.originStation.name} → ${schedule.destinationStation.name})`;
|
||||
throw new DeleteOperationException('Schedule', scheduleName, constraints);
|
||||
}
|
||||
}
|
||||
|
||||
await this.prisma.tripStopTime.deleteMany({ where: { scheduleId: id } });
|
||||
@@ -363,30 +365,49 @@ export class SchedulesService {
|
||||
await this.prisma.tripLiveStatus.deleteMany({ where: { scheduleId: id } });
|
||||
await this.prisma.menuItem.deleteMany({ where: { scheduleId: id } });
|
||||
await this.prisma.journeySegment.deleteMany({ where: { scheduleId: id } });
|
||||
|
||||
// Delete travel packages that reference this schedule (required fields cannot be nulled)
|
||||
// First get packages that reference this schedule
|
||||
const packagesToDelete = await this.prisma.travelPackage.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
{ outboundScheduleId: id },
|
||||
{ returnScheduleId: id }
|
||||
]
|
||||
},
|
||||
select: { id: true }
|
||||
|
||||
// Delete bookings and all their children
|
||||
const bookings = await this.prisma.booking.findMany({
|
||||
where: { OR: [{ scheduleId: id }, { returnScheduleId: id }] },
|
||||
select: { id: true },
|
||||
});
|
||||
if (bookings.length > 0) {
|
||||
const bookingIds = bookings.map(b => b.id);
|
||||
// Leaf tables first
|
||||
const tickets = await this.prisma.ticket.findMany({ where: { bookingId: { in: bookingIds } }, select: { id: true } });
|
||||
if (tickets.length > 0) {
|
||||
await this.prisma.gateValidationLog.deleteMany({ where: { ticketId: { in: tickets.map(t => t.id) } } });
|
||||
}
|
||||
await this.prisma.ticket.deleteMany({ where: { bookingId: { in: bookingIds } } });
|
||||
const foodOrders = await this.prisma.foodOrder.findMany({ where: { bookingId: { in: bookingIds } }, select: { id: true } });
|
||||
if (foodOrders.length > 0) {
|
||||
await this.prisma.foodOrderItem.deleteMany({ where: { orderId: { in: foodOrders.map(o => o.id) } } });
|
||||
}
|
||||
await this.prisma.foodOrder.deleteMany({ where: { bookingId: { in: bookingIds } } });
|
||||
const paymentIntents = await this.prisma.paymentIntent.findMany({ where: { bookingId: { in: bookingIds } }, select: { id: true } });
|
||||
if (paymentIntents.length > 0) {
|
||||
await this.prisma.paymentRefund.deleteMany({ where: { paymentIntentId: { in: paymentIntents.map(p => p.id) } } });
|
||||
}
|
||||
await this.prisma.paymentIntent.deleteMany({ where: { bookingId: { in: bookingIds } } });
|
||||
await this.prisma.bookingSeat.deleteMany({ where: { bookingId: { in: bookingIds } } });
|
||||
await this.prisma.agentBooking.deleteMany({ where: { bookingId: { in: bookingIds } } });
|
||||
await this.prisma.bookingModification.deleteMany({ where: { bookingId: { in: bookingIds } } });
|
||||
await this.prisma.bookingCancellation.deleteMany({ where: { bookingId: { in: bookingIds } } });
|
||||
await this.prisma.baggageBooking.deleteMany({ where: { bookingId: { in: bookingIds } } });
|
||||
await this.prisma.excessBaggageCharge.deleteMany({ where: { bookingId: { in: bookingIds } } });
|
||||
await this.prisma.journey.deleteMany({ where: { bookingId: { in: bookingIds } } });
|
||||
await this.prisma.booking.deleteMany({ where: { id: { in: bookingIds } } });
|
||||
}
|
||||
|
||||
// Delete travel packages that reference this schedule
|
||||
const packagesToDelete = await this.prisma.travelPackage.findMany({
|
||||
where: { OR: [{ outboundScheduleId: id }, { returnScheduleId: id }] },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
// Delete price tiers first (they have foreign key to packages)
|
||||
if (packagesToDelete.length > 0) {
|
||||
const packageIds = packagesToDelete.map(p => p.id);
|
||||
await this.prisma.packagePriceTier.deleteMany({
|
||||
where: { packageId: { in: packageIds } }
|
||||
});
|
||||
|
||||
// Now delete the packages
|
||||
await this.prisma.travelPackage.deleteMany({
|
||||
where: { id: { in: packageIds } }
|
||||
});
|
||||
await this.prisma.packagePriceTier.deleteMany({ where: { packageId: { in: packageIds } } });
|
||||
await this.prisma.travelPackage.deleteMany({ where: { id: { in: packageIds } } });
|
||||
}
|
||||
return this.prisma.trainSchedule.delete({ where: { id } });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user