Merge pull request #988 from Tria-plc/tests

fix(bookings): apply promo discount to client-subtotal totals in gues…
This commit is contained in:
mulish77
2026-07-28 09:58:39 +03:00
committed by GitHub

View File

@@ -294,8 +294,12 @@ export class GuestBookingService {
let displayTotalMinor: number;
let resolvedTotalMinor: number;
// True when the total came from a client-summed subtotal (per-seat sum or reviewedTotalMinor),
// which the portal computes UNDISCOUNTED — the promo must still be applied to it (H-13).
let usedClientSubtotal = false;
if (allFaresProvided && !isPackageOneway) {
displayTotalMinor = passengersWithFares.reduce((sum, p) => sum + p.fareMinor, 0);
usedClientSubtotal = true;
} else if (isPackageOneway && dto.reviewedTotalMinor != null) {
displayTotalMinor = dto.reviewedTotalMinor;
if (seatedPassengers.length > 0) {
@@ -306,6 +310,7 @@ export class GuestBookingService {
}
} else if (dto.reviewedTotalMinor != null && dto.reviewedTotalMinor > 0) {
displayTotalMinor = dto.reviewedTotalMinor;
usedClientSubtotal = true;
} else {
// fare engine returns ETB — convert forward to display currency
const etbTotal = Math.max(0, totalBaseFareMinor - discountMinor);
@@ -313,6 +318,18 @@ export class GuestBookingService {
? await this.currencyService.convertAmount(etbTotal, Currency.ETB, displayCurrency)
: etbTotal;
}
// H-13: the portal sums UNDISCOUNTED per-passenger fares into a client subtotal, silently
// dropping the promo the fare engine recognized. Apply the authoritative discount now so the
// customer is charged the discounted price. The non-client-subtotal branch above already
// nets the discount out of etbTotal, so it's excluded here to avoid double-subtracting.
if (usedClientSubtotal && discountMinor > 0) {
const discountDisplayMinor = displayCurrency !== Currency.ETB
? await this.currencyService.convertAmount(discountMinor, Currency.ETB, displayCurrency)
: discountMinor;
displayTotalMinor = Math.max(0, displayTotalMinor - discountDisplayMinor);
}
resolvedTotalMinor = displayCurrency !== Currency.ETB
? await this.currencyService.convertAmount(displayTotalMinor, displayCurrency, Currency.ETB)
: displayTotalMinor;
@@ -834,17 +851,16 @@ export class GuestBookingService {
const allRTFaresProvided = rtSeatedPassengers.length > 0 &&
rtSeatedPassengers.every(p => p.seatFareMinor != null && p.returnSeatFareMinor != null);
// True when the total came from a client-summed subtotal (per-seat sum or reviewedTotalMinor),
// which the portal computes UNDISCOUNTED — the promo must still be applied to it (H-13).
let usedClientSubtotal = false;
if (allRTFaresProvided && !isPackageRoundTrip) {
// Server-computed sum is authoritative — prevents race-condition under-count.
displayTotalMinor = passengersWithFares.reduce((sum, p) => sum + p.outboundFareMinor + p.returnFareMinor, 0);
totalMinor = displayCurrency !== Currency.ETB
? await this.currencyService.convertAmount(displayTotalMinor, displayCurrency, Currency.ETB)
: displayTotalMinor;
usedClientSubtotal = true;
} else if (isPackageRoundTrip && dto.reviewedTotalMinor != null) {
displayTotalMinor = dto.reviewedTotalMinor;
totalMinor = displayCurrency !== Currency.ETB
? await this.currencyService.convertAmount(displayTotalMinor, displayCurrency, Currency.ETB)
: displayTotalMinor;
const seatedCount = passengersData.filter(p => p.seatId).length;
if (seatedCount > 0) {
const perSeatPerLeg = Math.round(dto.reviewedTotalMinor / (seatedCount * 2));
@@ -855,11 +871,24 @@ export class GuestBookingService {
}
} else if (dto.reviewedTotalMinor != null && dto.reviewedTotalMinor > 0) {
displayTotalMinor = dto.reviewedTotalMinor;
totalMinor = displayCurrency !== Currency.ETB
? await this.currencyService.convertAmount(displayTotalMinor, displayCurrency, Currency.ETB)
: displayTotalMinor;
usedClientSubtotal = true;
}
// H-13: the portal sums UNDISCOUNTED per-passenger fares into a client subtotal, silently
// dropping the promo the fare engine recognized. Apply the authoritative discount now so the
// customer is charged the discounted price. The no-override case above already starts from a
// discounted displayTotalMinor, so it's excluded here to avoid double-subtracting.
if (usedClientSubtotal && discountMinor > 0) {
const discountDisplayMinor = displayCurrency !== Currency.ETB
? await this.currencyService.convertAmount(discountMinor, Currency.ETB, displayCurrency)
: discountMinor;
displayTotalMinor = Math.max(0, displayTotalMinor - discountDisplayMinor);
}
totalMinor = displayCurrency !== Currency.ETB
? await this.currencyService.convertAmount(displayTotalMinor, displayCurrency, Currency.ETB)
: displayTotalMinor;
// C-1 guard: never charge less than the server-recomputed authoritative round-trip fare.
this.assertTotalNotUnderAuthoritative(totalMinor, authoritativeTotalMinor, 'createGuestRoundTripBooking');