fix: make sure calculations in booking flow are correct

This commit is contained in:
Simon Emanuelsson
2025-04-02 15:49:59 +02:00
committed by Michael Zetterberg
parent 3e0f503314
commit a222ecfc5c
28 changed files with 309 additions and 276 deletions

View File

@@ -31,7 +31,7 @@ import styles from "./rateSummary.module.css"
import type { Price } from "@/types/components/hotelReservation/price"
import type { RateSummaryProps } from "@/types/components/hotelReservation/selectRate/rateSummary"
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
import type { Rate } from "@/types/components/hotelReservation/selectRate/selectRate"
import { RateEnum } from "@/types/enums/rate"
import { RateTypeEnum } from "@/types/enums/rateType"
export default function RateSummary({ isUserLoggedIn }: RateSummaryProps) {
@@ -111,13 +111,13 @@ export default function RateSummary({ isUserLoggedIn }: RateSummaryProps) {
const payLater = intl.formatMessage({ id: "Pay later" })
const payNow = intl.formatMessage({ id: "Pay now" })
function getRateDetails(rate: Rate["rate"]) {
function getRateDetails(rate: RateEnum) {
switch (rate) {
case "change":
case RateEnum.change:
return `${freeBooking}, ${payNow}`
case "flex":
case RateEnum.flex:
return `${freeCancelation}, ${payLater}`
case "save":
case RateEnum.save:
default:
return `${nonRefundable}, ${payNow}`
}
@@ -243,19 +243,29 @@ export default function RateSummary({ isUserLoggedIn }: RateSummaryProps) {
total,
{ features, packages: roomPackages, product }
) => {
if (!("member" in product) || !product.member) {
return total
}
const memberPrice =
product.member.localPrice.pricePerStay
if (!memberPrice) {
const memberExists =
"member" in product && product.member
const publicExists =
"public" in product && product.public
if (!memberExists) {
if (!publicExists) {
return total
}
}
const price =
product.member?.localPrice.pricePerStay ||
product.public?.localPrice.pricePerStay
if (!price) {
return total
}
const hasSelectedPetRoom = roomPackages.includes(
RoomPackageCodeEnum.PET_ROOM
)
if (!hasSelectedPetRoom) {
return total + memberPrice
return total + price
}
const isPetRoom = features.find(
(feature) =>
@@ -265,7 +275,7 @@ export default function RateSummary({ isUserLoggedIn }: RateSummaryProps) {
isPetRoom && petRoomPackage
? Number(petRoomPackage.localPrice.totalPrice)
: 0
return total + memberPrice + petRoomPrice
return total + price + petRoomPrice
},
0
),

View File

@@ -34,24 +34,32 @@ export function calculateTotalPrice(
const isPetRoom = room.features.find(
(feature) => feature.code === RoomPackageCodeEnum.PET_ROOM
)
let petRoomPrice = 0
let petRoomPriceLocal = 0
if (
petRoomPackage &&
isPetRoom &&
room.packages.includes(RoomPackageCodeEnum.PET_ROOM)
) {
petRoomPrice = Number(petRoomPackage.localPrice.totalPrice)
petRoomPriceLocal = Number(petRoomPackage.localPrice.totalPrice)
}
let petRoomPriceRequested = 0
if (
petRoomPackage &&
isPetRoom &&
room.packages.includes(RoomPackageCodeEnum.PET_ROOM)
) {
petRoomPriceRequested = Number(petRoomPackage.requestedPrice.totalPrice)
}
total.local.currency = rate.localPrice.currency
total.local.price =
total.local.price + rate.localPrice.pricePerStay + petRoomPrice
total.local.price + rate.localPrice.pricePerStay + petRoomPriceLocal
if (rate.localPrice.regularPricePerStay) {
total.local.regularPrice =
(total.local.regularPrice || 0) +
rate.localPrice.regularPricePerStay +
petRoomPrice
petRoomPriceLocal
}
if (rate.requestedPrice) {
@@ -69,13 +77,13 @@ export function calculateTotalPrice(
total.requested.price =
total.requested.price +
rate.requestedPrice.pricePerStay +
petRoomPrice
petRoomPriceRequested
if (rate.requestedPrice.regularPricePerStay) {
total.requested.regularPrice =
(total.requested.regularPrice || 0) +
rate.requestedPrice.regularPricePerStay +
petRoomPrice
petRoomPriceRequested
}
}

View File

@@ -15,8 +15,8 @@ import { useRoomContext } from "@/contexts/SelectRate/Room"
import styles from "./selectedRoomPanel.module.css"
import type { Rate } from "@/types/components/hotelReservation/selectRate/selectRate"
import { CurrencyEnum } from "@/types/enums/currency"
import { RateEnum } from "@/types/enums/rate"
export default function SelectedRoomPanel() {
const intl = useIntl()
@@ -43,13 +43,13 @@ export default function SelectedRoomPanel() {
const payLater = intl.formatMessage({ id: "Pay later" })
const payNow = intl.formatMessage({ id: "Pay now" })
function getRateTitle(rate: Rate["rate"]) {
function getRateTitle(rate: RateEnum) {
switch (rate) {
case "change":
case RateEnum.change:
return `${freeBooking}, ${payNow}`
case "flex":
case RateEnum.flex:
return `${freeCancelation}, ${payLater}`
case "save":
case RateEnum.save:
default:
return `${nonRefundable}, ${payNow}`
}

View File

@@ -27,5 +27,5 @@ export function getBreakfastMessage(
return msgs.notIncluded
}
return msgs.noSelection
return msgs.notIncluded
}

View File

@@ -4,8 +4,6 @@ import { useIntl } from "react-intl"
import CampaignRateCard from "@scandic-hotels/design-system/CampaignRateCard"
import NoRateAvailableCard from "@scandic-hotels/design-system/NoRateAvailableCard"
import { useRatesStore } from "@/stores/select-rate"
import { useRoomContext } from "@/contexts/SelectRate/Room"
import useRateTitles from "@/hooks/booking/useRateTitles"
@@ -30,7 +28,6 @@ export default function Campaign({
const intl = useIntl()
const { roomAvailability, roomNr, selectedFilter, selectedRate } =
useRoomContext()
const bookingCode = useRatesStore((state) => state.booking.bookingCode)
const rateTitles = useRateTitles()
let isCampaignRate = false
@@ -85,11 +82,11 @@ export default function Campaign({
)
let bannerText = intl.formatMessage({ id: "Campaign" })
if (bookingCode) {
bannerText = bookingCode
if (product.bookingCode) {
bannerText = product.bookingCode
}
if (product.rateDefinition?.breakfastIncluded) {
if (product.rateDefinition.breakfastIncluded) {
bannerText = `${bannerText}${intl.formatMessage({ id: "Breakfast included" })}`
} else {
bannerText = `${bannerText}${intl.formatMessage({ id: "Breakfast excluded" })}`

View File

@@ -38,7 +38,7 @@ export default function Code({
return code.map((product) => {
let bannerText = ""
if (product.breakfastIncluded) {
if (product.rateDefinition.breakfastIncluded) {
bannerText = `${bookingCode}${intl.formatMessage({ id: "Breakfast included" })}`
} else {
bannerText = `${bookingCode}${intl.formatMessage({ id: "Breakfast excluded" })}`
@@ -141,12 +141,13 @@ export default function Code({
petRoomPackage
)
const comparisonRate = regularPricePerNight.totalPrice
? {
price: regularPricePerNight.totalPrice,
unit: localPrice.currency,
}
: undefined
const comparisonRate =
+regularPricePerNight.totalPrice > +pricePerNight.totalPrice
? {
price: regularPricePerNight.totalPrice,
unit: localPrice.currency,
}
: undefined
const isSelected = isSelectedPriceProduct(
product,

View File

@@ -72,7 +72,7 @@ export default function Redemptions({
const notEnoughPoints = rates.every((rate) => rate.isDisabled)
const firstRedemption = redemptions[0]
const bannerText = firstRedemption.breakfastIncluded
const bannerText = firstRedemption.rateDefinition.breakfastIncluded
? `${rewardNight}${breakfastIncluded}`
: `${rewardNight}${breakfastExcluded}`

View File

@@ -114,7 +114,7 @@ export default function Regular({
unit: `${standard!.localPrice.currency}/${night}`,
}
if (standardPricePerNight.totalRequestedPrice) {
if (standardPricePerNight.totalRequestedPrice && !isUserLoggedIn) {
approximateStandardRatePrice = standardPricePerNight.totalRequestedPrice
}
}

View File

@@ -10,34 +10,29 @@ export function isSelectedPriceProduct(
selectedRate: SelectedRate | null,
roomTypeCode: string
) {
if (!selectedRate) {
if (!selectedRate || roomTypeCode !== selectedRate.roomTypeCode) {
return false
}
const { member, public: standard } = product
let selectedRateMember: PriceProduct["member"] = null
if ("member" in selectedRate.product) {
selectedRateMember = selectedRate.product.member
let isSelected = false
if (
"member" in selectedRate.product &&
selectedRate.product.member &&
member
) {
isSelected = selectedRate.product.member.rateCode === member.rateCode
}
let selectedRatePublic: PriceProduct["public"] = null
if ("public" in selectedRate.product) {
selectedRatePublic = selectedRate.product.public
if (
"public" in selectedRate.product &&
selectedRate.product.public &&
standard
) {
isSelected = selectedRate.product.public.rateCode === standard.rateCode
}
const selectedRateIsMember =
member &&
selectedRateMember &&
member.rateCode === selectedRateMember.rateCode
const selectedRateIsPublic =
standard &&
selectedRatePublic &&
standard.rateCode === selectedRatePublic.rateCode
return !!(
(selectedRateIsMember || selectedRateIsPublic) &&
selectedRate.roomTypeCode === roomTypeCode
)
return isSelected
}
export function isSelectedCorporateCheque(