Package pricing issue resolution, passenger report updates

This commit is contained in:
Stephanos A
2026-07-17 23:48:49 +03:00
parent 1d9238fa39
commit eee14a5051
3 changed files with 12 additions and 14 deletions

View File

@@ -18,8 +18,8 @@ export class ReportsController {
return this.service.generateReport(dto);
}
@Get('occupancy')
@ApiOperation({ summary: 'Occupancy report for a specific schedule' })
@Get('passengers')
@ApiOperation({ summary: 'Passengers report for a specific schedule' })
getOccupancyReport(@Query('scheduleId') scheduleId: string) {
return this.service.getOccupancyBySchedule(scheduleId);
}

View File

@@ -949,10 +949,9 @@ export default function SeatsPage() {
return;
}
// No fare change — but for package bookings still sync the tier price to the
// actual berth fare (handles the case where the first seat picked matches the
// stored price but we still want it explicitly confirmed).
if (isPackageBooking && packageId && newFare !== packageTierPriceMinor) {
// No fare change — for package bookings still sync the tier price to the
// actual berth fare so the correct amount is always stored.
if (isPackageBooking && packageId) {
setPackageContext(
packageId,
priceTierId ?? '',
@@ -962,6 +961,8 @@ export default function SeatsPage() {
packageDepartureStationName ?? undefined,
);
}
} else if (isPackageBooking && packageId) {
// newFare is null (seat class data unavailable) — leave stored price as-is.
}
commitSeatAssignment(seatId);

View File

@@ -401,7 +401,6 @@ const PKG_CHILDREN_PER_ADULT = 5;
function PassengerCountModal({
tier,
minPriceMinor,
onClose,
onConfirm,
loading,
@@ -410,7 +409,6 @@ function PassengerCountModal({
stations,
}: {
tier: PriceTier;
minPriceMinor: number;
onClose: () => void;
onConfirm: (adultCount: number, childCount: number, departureStationId: string, departureStationName: string) => void;
loading: boolean;
@@ -423,11 +421,9 @@ function PassengerCountModal({
const [departureStationId, setDepartureStationId] = useState('');
const [showStationError, setShowStationError] = useState(false);
const remaining = tier.availableSeats - tier.bookedSeats;
// First child per adult travels free (no seat); additional children pay full adult fare
const freeChildren = Math.min(childCount, adultCount);
const paidChildren = Math.max(0, childCount - adultCount);
// Only paid children need seats; free children share with an adult
const totalMinor = (adultCount * minPriceMinor + paidChildren * minPriceMinor) * priceMultiplier;
const totalMinor = (adultCount * tier.priceMinor + paidChildren * tier.priceMinor) * priceMultiplier;
return (
<>
@@ -444,7 +440,7 @@ function PassengerCountModal({
<div className="px-6 py-3 bg-primary/5 border-b border-primary/10">
<p className="text-xs text-gray-400 uppercase tracking-wide font-semibold">Coach type</p>
<p className="text-sm font-bold text-gray-900 dark:text-white mt-0.5">{tier.seatClass?.coachType?.name ?? tier.label.trim()}</p>
<p className="text-xs text-gray-400 mt-0.5">{remaining} seats remaining · from {formatPrice(minPriceMinor * priceMultiplier, tier.currency)} per adult · 1st child per adult free (no seat)</p>
<p className="text-xs text-gray-400 mt-0.5">{remaining} seats remaining · from {formatPrice(tier.priceMinor * priceMultiplier, tier.currency)} per adult · 1st child per adult free (no seat)</p>
</div>
<div className="px-6 py-5 space-y-4">
@@ -646,6 +642,8 @@ export default function PackageDetailPage() {
);
// Store per-adult tier price (×1 leg); review page applies round-trip multiplier and child pricing
// Store the group's cheapest tier price as a placeholder — the actual berth fare
// (Upper/Middle/Lower) will be resolved and saved when the user picks a seat.
setPackageContext(id, representativeTier.id, representativeTier.priceMinor, pkg.name, departureStationId, departureStationName);
router.push("/booking/passengers");
@@ -696,7 +694,6 @@ export default function PackageDetailPage() {
{passengerModalOpen && representativeTier && (
<PassengerCountModal
tier={representativeTier}
minPriceMinor={selectedGroup?.minPrice ?? representativeTier.priceMinor}
onClose={() => { setPassengerModalOpen(false); setBookingContextError(null); }}
onConfirm={handleBookNow}
loading={bookingContextLoading}
@@ -896,7 +893,7 @@ export default function PackageDetailPage() {
{/* ── Sidebar — desktop ── */}
<div className="hidden lg:block">
<div className="sticky top-20">
<div>
<PriceTiersPanel
tiers={pkg.priceTiers}
onBookNow={(coachTypeId) => { setSelectedCoachTypeId(coachTypeId); setPassengerModalOpen(true); }}