Fix package price

This commit is contained in:
Roba Boru
2026-07-20 23:08:32 +03:00
parent 73f067aeed
commit 296e3af7dd
7 changed files with 81 additions and 41 deletions

View File

@@ -162,29 +162,39 @@ export class PaymentsService {
}
/**
* Returns the correct totalMinor for a booking, accounting for package round-trip bookings
* where totalMinor may have been stored as a single-leg amount before the server fix.
* A package round-trip booking has packageId set, bookingType ROUND_TRIP, and
* totalMinor equal to a single-leg fare (i.e. seats split evenly across 2 legs).
* Returns the correct totalMinor (in ETB) for a booking, accounting for package round-trip
* bookings where totalMinor may have been stored as a single-leg amount before the server fix.
*/
private async resolveBookingTotal(booking: { id: string; totalMinor: number; bookingType: string; packageId?: string | null; priceTierId?: string | null }): Promise<number> {
private async resolveBookingTotal(booking: {
id: string;
totalMinor: number;
bookingType: string;
packageId?: string | null;
priceTierId?: string | null;
displayTotalMinor?: number | null;
}): Promise<number> {
if (!booking.packageId || !booking.priceTierId || booking.bookingType !== 'ROUND_TRIP') {
return booking.totalMinor;
}
// For package round-trip bookings, recompute from the tier price to handle
// bookings created before the server fix stored the full round-trip total.
// New bookings store displayTotalMinor from the frontend's reviewedTotalMinor; their
// totalMinor was already computed in ETB at creation time — no recomputation needed.
if (booking.displayTotalMinor != null && booking.displayTotalMinor > 0) {
return booking.totalMinor;
}
// Legacy path: old bookings may have stored a single-leg totalMinor — recompute from tier.
const tier = await this.prisma.packagePriceTier.findUnique({ where: { id: booking.priceTierId } });
if (!tier) return booking.totalMinor;
// Count adults and children from booking seats
const seats = await this.prisma.bookingSeat.findMany({ where: { bookingId: booking.id, leg: 1 }, select: { passengerCategory: true } });
const adultCount = seats.filter(s => s.passengerCategory === 'ADULT').length || 1;
const childCount = seats.filter(s => s.passengerCategory === 'CHILD').length;
const adultFareMinor = tier.priceMinor * 2; // round-trip = 2 legs
// tier.priceMinor may be in a non-ETB currency — convert to ETB so the result is
// always in the same units as totalMinor (which is always the ETB canonical).
const rawFare = tier.priceMinor * 2;
const adultFareMinor = tier.currency && (tier.currency as string) !== 'ETB'
? await this.currencyService.convertAmount(rawFare, tier.currency as any, 'ETB' as any)
: rawFare;
const childFareMinor = Math.round(adultFareMinor * 0.1);
const correctTotal = adultCount * adultFareMinor + childCount * childFareMinor;
// If stored total already matches the correct round-trip total, use it as-is.
// If it's roughly half (single-leg), use the recomputed value.
return correctTotal;
return adultCount * adultFareMinor + childCount * childFareMinor;
}
async initiatePayment(