mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
Refactored the whole app based on the requirements shared
This commit is contained in:
@@ -2,7 +2,7 @@ import { Injectable, NotFoundException, BadRequestException } from '@nestjs/comm
|
||||
import { PrismaService } from '../../common/prisma.service';
|
||||
import { SeatsService } from '../seats/seats.service';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { CreateBookingDto } from './bookings.dto';
|
||||
import { CreateBookingDto, ModifyBookingDto, CancelBookingDto } from './bookings.dto';
|
||||
import { Cron, CronExpression } from '@nestjs/schedule';
|
||||
import { SearchService } from '../search/search.service';
|
||||
|
||||
@@ -20,13 +20,44 @@ export class BookingsService {
|
||||
if (!hold || hold.expiresAt < new Date()) throw new BadRequestException('Seat hold expired');
|
||||
const trip = await this.prisma.trip.findUnique({ where: { id: dto.tripId }, include: { originStation: true, destinationStation: true } });
|
||||
if (!trip) throw new NotFoundException('Trip not found');
|
||||
const fareQuote = await this.searchService.getFareQuote({ tripId: dto.tripId, serviceClass: dto.serviceClass ?? 'ECONOMY', passengerCount: dto.passengers.length, promoCode: dto.promoCode, loyaltyRedemptionPoints: dto.loyaltyRedemptionPoints });
|
||||
|
||||
let seatIds: string[];
|
||||
if (dto.autoAssign) {
|
||||
seatIds = await this.seatsService.autoAssignSeats(
|
||||
dto.tripId,
|
||||
dto.passengers.length,
|
||||
dto.serviceClass ?? 'ECONOMY_REGULAR',
|
||||
);
|
||||
await this.seatsService.confirmSeats(seatIds);
|
||||
} else {
|
||||
seatIds = dto.passengers.map((p) => p.seatId);
|
||||
}
|
||||
|
||||
const fareQuote = await this.searchService.getFareQuote({ tripId: dto.tripId, serviceClass: dto.serviceClass ?? 'ECONOMY_REGULAR', passengerCount: dto.passengers.length, promoCode: dto.promoCode, loyaltyRedemptionPoints: dto.loyaltyRedemptionPoints });
|
||||
const booking = await this.prisma.booking.create({
|
||||
data: { bookingRef: generateRef(), passengerId: dto.passengerId, tripId: dto.tripId, status: 'PENDING_PAYMENT', totalMinor: fareQuote.totalMinor, seats: { create: dto.passengers.map((p) => ({ seatId: p.seatId, passengerName: p.fullName, idDocumentType: p.idDocumentType, idDocumentNumber: p.idDocumentNumber })) } },
|
||||
data: {
|
||||
bookingRef: generateRef(),
|
||||
passengerId: dto.passengerId,
|
||||
tripId: dto.tripId,
|
||||
status: 'PENDING_PAYMENT',
|
||||
totalMinor: fareQuote.totalMinor,
|
||||
bookingType: dto.bookingType ?? 'ONE_WAY',
|
||||
seats: { create: dto.passengers.map((p, i) => ({ seatId: seatIds[i], passengerName: p.fullName, idDocumentType: p.idDocumentType, idDocumentNumber: p.idDocumentNumber })) }
|
||||
},
|
||||
include: { seats: { include: { seat: true } }, trip: { include: { originStation: true, destinationStation: true, service: true } } },
|
||||
});
|
||||
this.eventEmitter.emit('booking.created', { booking });
|
||||
return booking;
|
||||
return {
|
||||
...booking,
|
||||
fareBreakdown: {
|
||||
baseFare: fareQuote.baseFareMinor / 100,
|
||||
discount: fareQuote.discountMinor / 100,
|
||||
loyaltyRedemption: fareQuote.loyaltyRedemptionMinor / 100,
|
||||
taxesFees: fareQuote.taxesFeesMinor / 100,
|
||||
total: fareQuote.totalMinor / 100,
|
||||
currency: fareQuote.currency
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async getByRef(bookingRef: string) {
|
||||
@@ -37,6 +68,7 @@ export class BookingsService {
|
||||
bookingRef: booking.bookingRef,
|
||||
status: booking.status,
|
||||
totalFare: booking.totalMinor / 100,
|
||||
bookingType: booking.bookingType,
|
||||
createdAt: booking.createdAt,
|
||||
trip: {
|
||||
number: booking.trip.service.number,
|
||||
@@ -53,12 +85,55 @@ export class BookingsService {
|
||||
};
|
||||
}
|
||||
|
||||
async cancel(bookingRef: string) {
|
||||
const booking = await this.prisma.booking.findUnique({ where: { bookingRef }, include: { seats: true } });
|
||||
async modify(dto: ModifyBookingDto) {
|
||||
const booking = await this.prisma.booking.findUnique({ where: { bookingRef: dto.bookingRef }, include: { seats: true, trip: true } });
|
||||
if (!booking) throw new NotFoundException('Booking not found');
|
||||
if (booking.status === 'CONFIRMED') throw new BadRequestException('Use refund for confirmed bookings');
|
||||
if (booking.status !== 'CONFIRMED') throw new BadRequestException('Only confirmed bookings can be modified');
|
||||
if (booking.trip.departureAt < new Date()) throw new BadRequestException('Cannot modify past bookings');
|
||||
|
||||
const oldSeats = booking.seats.map(s => s.seatId);
|
||||
const fareAdjustment = 0;
|
||||
|
||||
await this.prisma.bookingModification.create({
|
||||
data: {
|
||||
bookingId: booking.id,
|
||||
modifiedBy: booking.passengerId,
|
||||
modificationType: 'SEAT_CHANGE',
|
||||
oldData: { tripId: booking.tripId, seatIds: oldSeats },
|
||||
newData: { tripId: dto.newTripId, seatIds: dto.newSeatIds },
|
||||
fareAdjustment,
|
||||
reason: dto.reason
|
||||
}
|
||||
});
|
||||
|
||||
await this.seatsService.releaseSeats(oldSeats);
|
||||
await this.seatsService.confirmSeats(dto.newSeatIds);
|
||||
|
||||
return { modified: true, bookingRef: dto.bookingRef };
|
||||
}
|
||||
|
||||
async cancel(bookingRef: string, reason?: string) {
|
||||
const booking = await this.prisma.booking.findUnique({ where: { bookingRef }, include: { seats: true, paymentIntent: true } });
|
||||
if (!booking) throw new NotFoundException('Booking not found');
|
||||
if (booking.status === 'CANCELLED') throw new BadRequestException('Booking already cancelled');
|
||||
|
||||
const refundAmount = booking.status === 'CONFIRMED' ? Math.floor(booking.totalMinor * 0.8) : 0;
|
||||
|
||||
await this.prisma.bookingCancellation.create({
|
||||
data: {
|
||||
bookingId: booking.id,
|
||||
cancelledBy: booking.passengerId,
|
||||
reason,
|
||||
refundAmount,
|
||||
refundMethod: booking.paymentIntent?.method ?? 'ORIGINAL',
|
||||
refundStatus: 'PENDING'
|
||||
}
|
||||
});
|
||||
|
||||
await this.seatsService.releaseSeats(booking.seats.map((s) => s.seatId));
|
||||
return this.prisma.booking.update({ where: { bookingRef }, data: { status: 'CANCELLED' } });
|
||||
await this.prisma.booking.update({ where: { bookingRef }, data: { status: 'CANCELLED' } });
|
||||
|
||||
return { cancelled: true, refundAmount: refundAmount / 100, currency: 'ETB' };
|
||||
}
|
||||
|
||||
@Cron(CronExpression.EVERY_MINUTE)
|
||||
|
||||
Reference in New Issue
Block a user