Package payment amount fixes

This commit is contained in:
Stephanos A
2026-07-06 07:37:00 +03:00
parent dd4f2dcba9
commit 76bf81eec3
4 changed files with 170 additions and 52 deletions

View File

@@ -17,6 +17,25 @@ 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.
* tierPriceMinor is the per-leg per-adult price from PackagePackagePriceTier.
*/
function resolvePackageRoundTripTotal(
booking: { totalMinor: number; bookingType: string; packageId?: string | null },
tierPriceMinor: number | null | undefined,
adultCount: number,
childCount: number,
): number {
if (!booking.packageId || booking.bookingType !== 'ROUND_TRIP' || !tierPriceMinor) {
return booking.totalMinor;
}
const adultFareMinor = tierPriceMinor * 2;
const childFareMinor = Math.round(adultFareMinor * 0.1);
return adultCount * adultFareMinor + childCount * childFareMinor;
}
function calculateAge(dateOfBirth: Date): number {
const today = new Date();
let age = today.getFullYear() - dateOfBirth.getFullYear();
@@ -82,6 +101,7 @@ export class BookingsService {
schedule: { include: { originStation: true, destinationStation: true, train: true } },
paymentIntent: true,
seats: { include: { seat: true } },
priceTier: { select: { priceMinor: true } },
},
}),
this.prisma.booking.count({ where }),
@@ -92,7 +112,7 @@ export class BookingsService {
id: booking.id,
bookingRef: booking.bookingRef,
status: booking.status,
totalMinor: booking.totalMinor,
totalMinor: resolvePackageRoundTripTotal(booking, (booking as any).priceTier?.priceMinor, booking.adultCount, booking.childCount),
currency: 'ETB',
displayCurrency: booking.displayCurrency,
displayTotalMinor: booking.displayTotalMinor,
@@ -161,6 +181,7 @@ export class BookingsService {
schedule: { include: { originStation: true, destinationStation: true, train: true } },
paymentIntent: true,
seats: { include: { seat: true } },
priceTier: { select: { priceMinor: true } },
},
}),
this.prisma.booking.count({ where }),
@@ -171,7 +192,7 @@ export class BookingsService {
id: booking.id,
bookingRef: booking.bookingRef,
status: booking.status,
totalMinor: booking.totalMinor,
totalMinor: resolvePackageRoundTripTotal(booking, (booking as any).priceTier?.priceMinor, booking.adultCount, booking.childCount),
currency: 'ETB',
displayCurrency: booking.displayCurrency,
displayTotalMinor: booking.displayTotalMinor,
@@ -295,6 +316,7 @@ export class BookingsService {
schedule: { include: { originStation: true, destinationStation: true, train: true } },
paymentIntent: true,
seats: { include: { seat: true } },
priceTier: { select: { priceMinor: true } },
},
}),
this.prisma.booking.count({ where: bookingPkgWhere }),
@@ -315,7 +337,7 @@ export class BookingsService {
const uniquePassengers = Array.from(new Map(passengerDetails.map((p: any) => [p.name, p])).values());
return {
id: booking.id, bookingRef: booking.bookingRef, status: booking.status,
totalMinor: booking.totalMinor, currency: 'ETB',
totalMinor: resolvePackageRoundTripTotal(booking, booking.priceTier?.priceMinor, booking.adultCount, booking.childCount), currency: 'ETB',
displayCurrency: booking.displayCurrency, displayTotalMinor: booking.displayTotalMinor,
contactEmail: booking.contactEmail, contactPhone: booking.contactPhone,
bookingType: booking.bookingType, packageId: booking.packageId, isPackageBooking: true,
@@ -374,7 +396,7 @@ export class BookingsService {
paymentIntent: true,
seats: { include: { seat: true } },
package: { select: { id: true, name: true, code: true } },
priceTier: { select: { id: true, label: true } },
priceTier: { select: { id: true, label: true, priceMinor: true } },
},
}),
this.prisma.booking.count({ where }),
@@ -410,7 +432,7 @@ export class BookingsService {
id: booking.id,
bookingRef: booking.bookingRef,
status: booking.status,
totalMinor: booking.totalMinor,
totalMinor: resolvePackageRoundTripTotal(booking, (booking as any).priceTier?.priceMinor, booking.adultCount, booking.childCount),
currency: 'ETB',
displayCurrency: booking.displayCurrency,
displayTotalMinor: booking.displayTotalMinor,
@@ -639,12 +661,14 @@ export class BookingsService {
if (dto.packageId && dto.priceTierId) {
const pkgFare = await this.calculatePackageFare(dto.priceTierId, adultCount, childCount);
// pkgFare covers one leg; round-trip = both legs combined
const roundTripTotal = pkgFare.totalMinor * 2;
// Split evenly across both legs for per-seat fare recording
const halfMinor = Math.round(pkgFare.baseFareMinor / 2);
outboundFare = { ...pkgFare, baseFareMinor: halfMinor, totalBaseFareMinor: Math.round(pkgFare.totalBaseFareMinor / 2) };
returnFare = { ...pkgFare, baseFareMinor: pkgFare.baseFareMinor - halfMinor, totalBaseFareMinor: pkgFare.totalBaseFareMinor - Math.round(pkgFare.totalBaseFareMinor / 2) };
combinedBaseFareMinor = pkgFare.totalBaseFareMinor;
totalMinor = pkgFare.totalMinor;
combinedBaseFareMinor = pkgFare.totalBaseFareMinor * 2;
totalMinor = roundTripTotal;
} else {
[outboundFare, returnFare] = await Promise.all([
this.calculateFare(dto.scheduleId, dto.seatClassId, outboundOriginStop, outboundDestStop, passengersData[0]?.nationality, adultCount, childCount),
@@ -1385,6 +1409,7 @@ export class BookingsService {
schedule: { include: { originStation: true, destinationStation: true, train: true } },
seats: { include: { seat: { include: { coach: { include: { coachType: { include: { seatClasses: true } } } } } } } },
paymentIntent: true, tickets: { take: 1 },
priceTier: { select: { priceMinor: true } },
},
});
@@ -1447,7 +1472,7 @@ export class BookingsService {
return {
id: booking.id, bookingRef: booking.bookingRef, status: booking.status,
totalMinor: booking.totalMinor, currency: 'ETB',
totalMinor: resolvePackageRoundTripTotal(booking, (booking as any).priceTier?.priceMinor, booking.adultCount, booking.childCount), currency: 'ETB',
adultCount: booking.adultCount, childCount: booking.childCount,
displayCurrency: booking.displayCurrency, displayTotalMinor: booking.displayTotalMinor ?? undefined,
bookingType: booking.bookingType,