Merged in feat/refactor-enter-details-price-calculation (pull request #3183)
feat: Refactor enter details price calculation * Refactor getTotalPrice and child functions * Move price calculations from helper file to specific file Approved-by: Linus Flood
This commit is contained in:
@@ -16,10 +16,10 @@ import {
|
||||
import { EnterDetailsStepEnum } from "../../stores/enter-details/enterDetailsStep"
|
||||
import {
|
||||
clearSessionStorage,
|
||||
getTotalPrice,
|
||||
readFromSessionStorage,
|
||||
writeToSessionStorage,
|
||||
} from "../../stores/enter-details/helpers"
|
||||
import { getTotalPrice } from "../../stores/enter-details/priceCalculations"
|
||||
import { isSameBooking } from "../../utils/isSameBooking"
|
||||
|
||||
import type { Lang } from "@scandic-hotels/common/constants/language"
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import { parsePhoneNumberFromString } from "libphonenumber-js"
|
||||
|
||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
import { RateTypeEnum } from "@scandic-hotels/common/constants/rateType"
|
||||
import { logger } from "@scandic-hotels/common/logger"
|
||||
|
||||
import { calculateRegularPrice } from "../../utils/calculateRegularPrice"
|
||||
import { sumPackages, sumPackagesRequestedPrice } from "../../utils/SelectRate"
|
||||
import { detailsStorageName } from "."
|
||||
|
||||
import type { Product } from "@scandic-hotels/trpc/types/roomAvailability"
|
||||
import type { User } from "@scandic-hotels/trpc/types/user"
|
||||
|
||||
import type { Price } from "../../types/price"
|
||||
import type { PersistedState, RoomState } from "./types"
|
||||
|
||||
export function extractGuestFromUser(user: User) {
|
||||
@@ -34,191 +28,6 @@ export function extractGuestFromUser(user: User) {
|
||||
}
|
||||
}
|
||||
|
||||
function add(...nums: (number | string | undefined)[]) {
|
||||
return nums.reduce((total: number, num) => {
|
||||
if (typeof num === "undefined") {
|
||||
num = 0
|
||||
}
|
||||
total = total + parseInt(`${num}`)
|
||||
return total
|
||||
}, 0)
|
||||
}
|
||||
|
||||
export function getRoomPrice(
|
||||
roomRate: Product,
|
||||
isMember: boolean,
|
||||
pointsCurrency?: CurrencyEnum
|
||||
) {
|
||||
if (isMember && "member" in roomRate && roomRate.member) {
|
||||
let publicRate
|
||||
if (
|
||||
"public" in roomRate &&
|
||||
roomRate.public?.rateType === RateTypeEnum.Regular
|
||||
) {
|
||||
publicRate = roomRate.public
|
||||
}
|
||||
return {
|
||||
perNight: {
|
||||
requested: roomRate.member.requestedPrice
|
||||
? {
|
||||
currency: roomRate.member.requestedPrice.currency,
|
||||
price: roomRate.member.requestedPrice.pricePerNight,
|
||||
}
|
||||
: undefined,
|
||||
local: {
|
||||
currency: roomRate.member.localPrice.currency,
|
||||
price: roomRate.member.localPrice.pricePerNight,
|
||||
regularPrice:
|
||||
publicRate?.localPrice.pricePerStay ||
|
||||
roomRate.member.localPrice.regularPricePerNight,
|
||||
},
|
||||
},
|
||||
perStay: {
|
||||
requested: roomRate.member.requestedPrice
|
||||
? {
|
||||
currency: roomRate.member.requestedPrice.currency,
|
||||
price: roomRate.member.requestedPrice.pricePerStay,
|
||||
}
|
||||
: undefined,
|
||||
local: {
|
||||
currency: roomRate.member.localPrice.currency,
|
||||
price: roomRate.member.localPrice.pricePerStay,
|
||||
regularPrice:
|
||||
publicRate?.localPrice.pricePerStay ||
|
||||
roomRate.member.localPrice.regularPricePerStay,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if ("public" in roomRate && roomRate.public) {
|
||||
return {
|
||||
perNight: {
|
||||
requested: roomRate.public.requestedPrice
|
||||
? {
|
||||
currency: roomRate.public.requestedPrice.currency,
|
||||
price: roomRate.public.requestedPrice.pricePerNight,
|
||||
}
|
||||
: undefined,
|
||||
local: {
|
||||
currency: roomRate.public.localPrice.currency,
|
||||
price: roomRate.public.localPrice.pricePerNight,
|
||||
regularPrice: roomRate.public.localPrice.regularPricePerNight,
|
||||
},
|
||||
},
|
||||
perStay: {
|
||||
requested: roomRate.public.requestedPrice
|
||||
? {
|
||||
currency: roomRate.public.requestedPrice.currency,
|
||||
price: roomRate.public.requestedPrice.pricePerStay,
|
||||
}
|
||||
: undefined,
|
||||
local: {
|
||||
currency: roomRate.public.localPrice.currency,
|
||||
price: roomRate.public.localPrice.pricePerStay,
|
||||
regularPrice: roomRate.public.localPrice.regularPricePerStay,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if ("corporateCheque" in roomRate) {
|
||||
return {
|
||||
perNight: {
|
||||
requested: roomRate.corporateCheque.requestedPrice
|
||||
? {
|
||||
currency: CurrencyEnum.CC,
|
||||
price: roomRate.corporateCheque.requestedPrice.numberOfCheques,
|
||||
additionalPrice:
|
||||
roomRate.corporateCheque.requestedPrice.additionalPricePerStay,
|
||||
additionalPriceCurrency:
|
||||
roomRate.corporateCheque.requestedPrice.currency ?? undefined,
|
||||
}
|
||||
: undefined,
|
||||
local: {
|
||||
currency: CurrencyEnum.CC,
|
||||
price: roomRate.corporateCheque.localPrice.numberOfCheques,
|
||||
additionalPrice:
|
||||
roomRate.corporateCheque.localPrice.additionalPricePerStay,
|
||||
additionalPriceCurrency:
|
||||
roomRate.corporateCheque.localPrice.currency ?? undefined,
|
||||
},
|
||||
},
|
||||
perStay: {
|
||||
requested: roomRate.corporateCheque.requestedPrice
|
||||
? {
|
||||
currency: CurrencyEnum.CC,
|
||||
price: roomRate.corporateCheque.requestedPrice.numberOfCheques,
|
||||
additionalPrice:
|
||||
roomRate.corporateCheque.requestedPrice.additionalPricePerStay,
|
||||
additionalPriceCurrency:
|
||||
roomRate.corporateCheque.requestedPrice.currency ?? undefined,
|
||||
}
|
||||
: undefined,
|
||||
local: {
|
||||
currency: CurrencyEnum.CC,
|
||||
price: roomRate.corporateCheque.localPrice.numberOfCheques,
|
||||
additionalPrice:
|
||||
roomRate.corporateCheque.localPrice.additionalPricePerStay,
|
||||
additionalPriceCurrency:
|
||||
roomRate.corporateCheque.localPrice.currency ?? undefined,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if ("voucher" in roomRate) {
|
||||
return {
|
||||
perNight: {
|
||||
requested: undefined,
|
||||
local: {
|
||||
currency: CurrencyEnum.Voucher,
|
||||
price: roomRate.voucher.numberOfVouchers,
|
||||
},
|
||||
},
|
||||
perStay: {
|
||||
requested: undefined,
|
||||
local: {
|
||||
currency: CurrencyEnum.Voucher,
|
||||
price: roomRate.voucher.numberOfVouchers,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if ("redemption" in roomRate) {
|
||||
return {
|
||||
// ToDo Handle perNight as undefined
|
||||
perNight: {
|
||||
requested: undefined,
|
||||
local: {
|
||||
currency: pointsCurrency ?? CurrencyEnum.POINTS,
|
||||
price: roomRate.redemption.localPrice.pointsPerStay,
|
||||
additionalPrice:
|
||||
roomRate.redemption.localPrice.additionalPricePerStay,
|
||||
additionalPriceCurrency:
|
||||
roomRate.redemption.localPrice.currency ?? undefined,
|
||||
},
|
||||
},
|
||||
perStay: {
|
||||
requested: undefined,
|
||||
local: {
|
||||
currency: pointsCurrency ?? CurrencyEnum.POINTS,
|
||||
price: roomRate.redemption.localPrice.pointsPerStay,
|
||||
additionalPrice:
|
||||
roomRate.redemption.localPrice.additionalPricePerStay,
|
||||
additionalPriceCurrency:
|
||||
roomRate.redemption.localPrice.currency ?? undefined,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Unable to calculate RoomPrice since user is neither a member or memberRate is missing, or publicRate is missing`
|
||||
)
|
||||
}
|
||||
|
||||
export const checkRoomProgress = (steps: RoomState["steps"]) => {
|
||||
return Object.values(steps)
|
||||
.filter(Boolean)
|
||||
@@ -267,430 +76,3 @@ export function clearSessionStorage() {
|
||||
}
|
||||
sessionStorage.removeItem(detailsStorageName)
|
||||
}
|
||||
|
||||
export function getAdditionalPrice(
|
||||
total: {
|
||||
local: {
|
||||
additionalPrice?: number
|
||||
additionalPriceCurrency?: CurrencyEnum
|
||||
}
|
||||
},
|
||||
adults: number,
|
||||
breakfast:
|
||||
| { localPrice: { price: number; currency?: CurrencyEnum } }
|
||||
| false
|
||||
| undefined,
|
||||
nights: number,
|
||||
packages:
|
||||
| { localPrice: { totalPrice: number; currency?: CurrencyEnum } }[]
|
||||
| null
|
||||
| undefined,
|
||||
additionalPrice = 0,
|
||||
additionalPriceCurrency?: CurrencyEnum | null | undefined
|
||||
) {
|
||||
const breakfastLocalPrice =
|
||||
(breakfast ? breakfast.localPrice.price : 0) * nights * adults
|
||||
const pkgsSum = sumPackages(packages || [])
|
||||
|
||||
total.local.additionalPrice = add(
|
||||
total.local.additionalPrice,
|
||||
additionalPrice,
|
||||
breakfastLocalPrice,
|
||||
pkgsSum.price
|
||||
)
|
||||
|
||||
if (!total.local.additionalPriceCurrency) {
|
||||
if (additionalPriceCurrency) {
|
||||
total.local.additionalPriceCurrency = additionalPriceCurrency
|
||||
} else if (breakfast && breakfast.localPrice.currency) {
|
||||
total.local.additionalPriceCurrency = breakfast.localPrice.currency
|
||||
} else if (pkgsSum.currency) {
|
||||
total.local.additionalPriceCurrency = pkgsSum.currency
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getRequestedAdditionalPrice(
|
||||
total: Price,
|
||||
adults: number,
|
||||
breakfast:
|
||||
| {
|
||||
requestedPrice?: { price: number; currency?: CurrencyEnum }
|
||||
}
|
||||
| false
|
||||
| undefined,
|
||||
nights: number,
|
||||
packages:
|
||||
| { requestedPrice: { totalPrice: number; currency?: CurrencyEnum } }[]
|
||||
| null
|
||||
| undefined,
|
||||
cheques: number,
|
||||
additionalPrice = 0,
|
||||
additionalPriceCurrency: CurrencyEnum | null | undefined
|
||||
) {
|
||||
if (!total.requested) {
|
||||
total.requested = {
|
||||
currency: CurrencyEnum.CC,
|
||||
price: 0,
|
||||
}
|
||||
}
|
||||
|
||||
total.requested.price = add(total.requested.price, cheques)
|
||||
|
||||
const breakfastRequestedPrice =
|
||||
(breakfast ? breakfast.requestedPrice?.price || 0 : 0) * nights * adults
|
||||
const pkgsSumRequested = sumPackagesRequestedPrice(packages)
|
||||
|
||||
total.requested.additionalPrice = add(
|
||||
total.requested.additionalPrice,
|
||||
additionalPrice,
|
||||
breakfastRequestedPrice,
|
||||
pkgsSumRequested.price
|
||||
)
|
||||
|
||||
if (!total.requested.additionalPriceCurrency) {
|
||||
if (additionalPriceCurrency) {
|
||||
total.requested.additionalPriceCurrency = additionalPriceCurrency
|
||||
} else if (pkgsSumRequested.currency) {
|
||||
total.requested.additionalPriceCurrency = pkgsSumRequested.currency
|
||||
} else if (breakfast && breakfast.requestedPrice) {
|
||||
total.requested.additionalPriceCurrency =
|
||||
breakfast.requestedPrice.currency
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type CorporateCheckRoom = PriceCalculationRoom & {
|
||||
roomRate: {
|
||||
corporateCheque: {
|
||||
localPrice: {
|
||||
numberOfCheques: number
|
||||
additionalPricePerStay: number
|
||||
currency?: CurrencyEnum
|
||||
}
|
||||
requestedPrice?: {
|
||||
numberOfCheques: number
|
||||
additionalPricePerStay: number
|
||||
currency?: CurrencyEnum
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getCorporateChequePrice(
|
||||
rooms: PriceCalculationRoom[],
|
||||
nights: number
|
||||
) {
|
||||
return rooms
|
||||
.filter(
|
||||
(room): room is CorporateCheckRoom => "corporateCheque" in room.roomRate
|
||||
)
|
||||
.reduce<Price>(
|
||||
(total, room) => {
|
||||
const corporateCheque = room.roomRate.corporateCheque
|
||||
|
||||
total.local.price = add(
|
||||
total.local.price,
|
||||
corporateCheque.localPrice.numberOfCheques
|
||||
)
|
||||
|
||||
getAdditionalPrice(
|
||||
total,
|
||||
room.adults,
|
||||
room.breakfast,
|
||||
nights,
|
||||
room.roomFeatures,
|
||||
corporateCheque.localPrice.additionalPricePerStay,
|
||||
corporateCheque.localPrice.currency
|
||||
)
|
||||
|
||||
if (corporateCheque.requestedPrice) {
|
||||
getRequestedAdditionalPrice(
|
||||
total,
|
||||
room.adults,
|
||||
room.breakfast,
|
||||
nights,
|
||||
room.roomFeatures,
|
||||
corporateCheque.requestedPrice.numberOfCheques,
|
||||
corporateCheque.requestedPrice?.additionalPricePerStay,
|
||||
corporateCheque.requestedPrice?.currency
|
||||
)
|
||||
}
|
||||
|
||||
return total
|
||||
},
|
||||
{
|
||||
local: {
|
||||
currency: CurrencyEnum.CC,
|
||||
price: 0,
|
||||
},
|
||||
requested: undefined,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
type VoucherRoom = PriceCalculationRoom & {
|
||||
roomRate: {
|
||||
voucher: {
|
||||
numberOfVouchers: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getVoucherPrice(rooms: PriceCalculationRoom[], nights: number) {
|
||||
return rooms
|
||||
.filter((room): room is VoucherRoom => "voucher" in room.roomRate)
|
||||
.reduce<Price>(
|
||||
(total, room) => {
|
||||
const voucher = room.roomRate.voucher
|
||||
|
||||
total.local.price = add(total.local.price, voucher.numberOfVouchers)
|
||||
|
||||
getAdditionalPrice(
|
||||
total,
|
||||
room.adults,
|
||||
room.breakfast,
|
||||
nights,
|
||||
room.roomFeatures
|
||||
)
|
||||
|
||||
return total
|
||||
},
|
||||
{
|
||||
local: {
|
||||
currency: CurrencyEnum.Voucher,
|
||||
price: 0,
|
||||
},
|
||||
requested: undefined,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
type PriceCalculationRoom = {
|
||||
adults: number
|
||||
breakfast:
|
||||
| {
|
||||
localPrice: { price: number; currency?: CurrencyEnum }
|
||||
requestedPrice?: { price: number; currency?: CurrencyEnum }
|
||||
}
|
||||
| false
|
||||
| undefined
|
||||
roomFeatures:
|
||||
| {
|
||||
localPrice: { totalPrice: number; currency?: CurrencyEnum }
|
||||
requestedPrice: { totalPrice: number; currency?: CurrencyEnum }
|
||||
}[]
|
||||
| null
|
||||
| undefined
|
||||
// We don't care about roomRate unless it's RedemptionProduct
|
||||
roomRate: object
|
||||
}
|
||||
type RedemptionRoom = PriceCalculationRoom & {
|
||||
roomRate: {
|
||||
redemption: {
|
||||
localPrice: {
|
||||
pointsPerStay: number
|
||||
additionalPricePerStay: number
|
||||
currency?: CurrencyEnum
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getRedemptionPrice(
|
||||
rooms: PriceCalculationRoom[],
|
||||
nights: number,
|
||||
pointsCurrency?: CurrencyEnum
|
||||
) {
|
||||
return rooms
|
||||
.filter((room): room is RedemptionRoom => "redemption" in room.roomRate)
|
||||
.reduce<Price>(
|
||||
(total, room) => {
|
||||
const redemption = room.roomRate.redemption
|
||||
|
||||
total.local.price = add(
|
||||
total.local.price,
|
||||
redemption.localPrice.pointsPerStay
|
||||
)
|
||||
|
||||
getAdditionalPrice(
|
||||
total,
|
||||
room.adults,
|
||||
room.breakfast,
|
||||
nights,
|
||||
room.roomFeatures,
|
||||
redemption.localPrice.additionalPricePerStay,
|
||||
redemption.localPrice.currency
|
||||
)
|
||||
|
||||
return total
|
||||
},
|
||||
{
|
||||
local: {
|
||||
currency: pointsCurrency ?? CurrencyEnum.POINTS,
|
||||
price: 0,
|
||||
},
|
||||
requested: undefined,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
type RegularPriceCalculationRoom = PriceCalculationRoom & {
|
||||
guest: {
|
||||
join: boolean
|
||||
membershipNo?: string | null
|
||||
}
|
||||
}
|
||||
type RegularRoomLocalPrice = {
|
||||
price: number
|
||||
currency: CurrencyEnum
|
||||
pricePerStay: number
|
||||
pricePerNight: number
|
||||
regularPricePerStay: number
|
||||
}
|
||||
type RegularRoomRequestedPrice = {
|
||||
price: number
|
||||
currency: CurrencyEnum
|
||||
pricePerStay: number
|
||||
}
|
||||
type GetRegularPriceRoom = RegularPriceCalculationRoom & {
|
||||
roomRate: {
|
||||
member: {
|
||||
localPrice: RegularRoomLocalPrice
|
||||
requestedPrice: RegularRoomRequestedPrice
|
||||
}
|
||||
public: {
|
||||
localPrice: RegularRoomLocalPrice
|
||||
requestedPrice: RegularRoomRequestedPrice
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getRegularPrice(
|
||||
rooms: RegularPriceCalculationRoom[],
|
||||
isMember: boolean,
|
||||
nights: number
|
||||
) {
|
||||
const totalPrice = rooms
|
||||
.filter(
|
||||
(room): room is GetRegularPriceRoom =>
|
||||
"member" in room.roomRate || "public" in room.roomRate
|
||||
)
|
||||
.reduce<Price>(
|
||||
(total, room, idx) => {
|
||||
const isMainRoomAndMember = idx === 0 && isMember
|
||||
const join = Boolean(room.guest.join || room.guest.membershipNo)
|
||||
const memberRate = "member" in room.roomRate && room.roomRate.member
|
||||
const publicRate = "public" in room.roomRate && room.roomRate.public
|
||||
const useMemberRate = (isMainRoomAndMember || join) && memberRate
|
||||
|
||||
const rate = useMemberRate ? memberRate : publicRate
|
||||
|
||||
if (!rate) {
|
||||
return total
|
||||
}
|
||||
|
||||
const breakfastLocalPrice =
|
||||
(room.breakfast ? room.breakfast.localPrice.price || 0 : 0) *
|
||||
nights *
|
||||
room.adults
|
||||
const pkgsSum = sumPackages(room.roomFeatures || [])
|
||||
const additionalCost = breakfastLocalPrice + pkgsSum.price
|
||||
|
||||
total.local.currency = rate.localPrice.currency
|
||||
total.local.price = add(
|
||||
total.local.price,
|
||||
rate.localPrice.pricePerStay,
|
||||
additionalCost
|
||||
)
|
||||
|
||||
if (rate.requestedPrice) {
|
||||
if (!total.requested) {
|
||||
total.requested = {
|
||||
currency: rate.requestedPrice.currency,
|
||||
price: 0,
|
||||
}
|
||||
}
|
||||
|
||||
const breakfastRequestedPrice =
|
||||
(room.breakfast ? (room.breakfast.requestedPrice?.price ?? 0) : 0) *
|
||||
nights *
|
||||
room.adults
|
||||
const pkgsSumRequested = sumPackagesRequestedPrice(room.roomFeatures)
|
||||
|
||||
total.requested.price = add(
|
||||
total.requested.price,
|
||||
rate.requestedPrice.pricePerStay,
|
||||
breakfastRequestedPrice,
|
||||
pkgsSumRequested.price
|
||||
)
|
||||
}
|
||||
|
||||
return calculateRegularPrice({
|
||||
total,
|
||||
useMemberRate: !!useMemberRate,
|
||||
regularMemberPrice: memberRate
|
||||
? {
|
||||
pricePerStay: memberRate.localPrice.pricePerNight,
|
||||
regularPricePerStay: memberRate.localPrice.regularPricePerStay,
|
||||
}
|
||||
: undefined,
|
||||
regularPublicPrice: publicRate
|
||||
? {
|
||||
pricePerStay: publicRate.localPrice.pricePerNight,
|
||||
regularPricePerStay: publicRate.localPrice.regularPricePerStay,
|
||||
}
|
||||
: undefined,
|
||||
additionalCost,
|
||||
})
|
||||
},
|
||||
{
|
||||
local: {
|
||||
currency: CurrencyEnum.Unknown,
|
||||
price: 0,
|
||||
regularPrice: 0,
|
||||
},
|
||||
requested: undefined,
|
||||
}
|
||||
)
|
||||
|
||||
if (
|
||||
totalPrice.local.regularPrice &&
|
||||
totalPrice.local.price >= totalPrice.local.regularPrice
|
||||
) {
|
||||
totalPrice.local.regularPrice = 0
|
||||
}
|
||||
|
||||
return totalPrice
|
||||
}
|
||||
|
||||
type TRoom = Pick<
|
||||
RoomState["room"],
|
||||
"adults" | "breakfast" | "guest" | "roomFeatures" | "roomRate"
|
||||
>
|
||||
|
||||
export function getTotalPrice(
|
||||
rooms: TRoom[],
|
||||
isMember: boolean,
|
||||
nights: number,
|
||||
pointsCurrency?: CurrencyEnum
|
||||
) {
|
||||
const hasCorpChqRates = rooms.some(
|
||||
(room) => "corporateCheque" in room.roomRate
|
||||
)
|
||||
if (hasCorpChqRates) {
|
||||
return getCorporateChequePrice(rooms, nights)
|
||||
}
|
||||
|
||||
const hasRedemptionRates = rooms.some((room) => "redemption" in room.roomRate)
|
||||
if (hasRedemptionRates) {
|
||||
return getRedemptionPrice(rooms, nights, pointsCurrency)
|
||||
}
|
||||
|
||||
const hasVoucherRates = rooms.some((room) => "voucher" in room.roomRate)
|
||||
if (hasVoucherRates) {
|
||||
return getVoucherPrice(rooms, nights)
|
||||
}
|
||||
|
||||
return getRegularPrice(rooms, isMember, nights)
|
||||
}
|
||||
|
||||
@@ -11,10 +11,9 @@ import { EnterDetailsStepEnum } from "./enterDetailsStep"
|
||||
import {
|
||||
checkRoomProgress,
|
||||
extractGuestFromUser,
|
||||
getRoomPrice,
|
||||
getTotalPrice,
|
||||
writeToSessionStorage,
|
||||
} from "./helpers"
|
||||
import { getRoomPrice, getTotalPrice } from "./priceCalculations"
|
||||
|
||||
import type { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
import type { Lang } from "@scandic-hotels/common/constants/language"
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
getRequestedAdditionalPrice,
|
||||
getTotalPrice,
|
||||
getVoucherPrice,
|
||||
} from "./helpers"
|
||||
} from "./priceCalculations"
|
||||
|
||||
type GetAdditionalPriceParams = Parameters<typeof getAdditionalPrice>
|
||||
describe("getAdditionalPrice", () => {
|
||||
@@ -920,48 +920,6 @@ describe("getCorporateChequePrice", () => {
|
||||
})
|
||||
})
|
||||
|
||||
it("does not return price for rooms without corporateCheque", () => {
|
||||
const nights = 2
|
||||
const rooms: GetCorporateChequePriceParams[0] = [
|
||||
{
|
||||
adults: 1,
|
||||
breakfast: false,
|
||||
roomFeatures: [],
|
||||
roomRate: {
|
||||
public: {
|
||||
localPrice: {
|
||||
pricePerStay: 500,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
adults: 1,
|
||||
breakfast: false,
|
||||
roomFeatures: [],
|
||||
roomRate: {
|
||||
corporateCheque: {
|
||||
localPrice: {
|
||||
numberOfCheques: 3,
|
||||
additionalPricePerStay: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const result = getCorporateChequePrice(rooms, nights)
|
||||
|
||||
expect(result).toEqual({
|
||||
local: {
|
||||
price: 3,
|
||||
currency: CurrencyEnum.CC,
|
||||
additionalPrice: 0,
|
||||
},
|
||||
requested: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
it("calculates combined price with breakfast and room features", () => {
|
||||
const nights = 3
|
||||
const rooms: GetCorporateChequePriceParams[0] = [
|
||||
@@ -1167,49 +1125,6 @@ describe("getRedemptionPrice", () => {
|
||||
})
|
||||
})
|
||||
|
||||
it("does not return price for room without redemption", () => {
|
||||
const nights = 3
|
||||
const result = getRedemptionPrice(
|
||||
[
|
||||
{
|
||||
adults: 1,
|
||||
breakfast: false,
|
||||
roomFeatures: [],
|
||||
roomRate: {
|
||||
public: {
|
||||
price: 150,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
adults: 1,
|
||||
breakfast: false,
|
||||
roomFeatures: [],
|
||||
roomRate: {
|
||||
redemption: {
|
||||
localPrice: {
|
||||
pointsPerStay: 150,
|
||||
currency: CurrencyEnum.POINTS,
|
||||
additionalPricePerStay: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
nights
|
||||
)
|
||||
|
||||
expect(result).toEqual({
|
||||
local: {
|
||||
price: 150,
|
||||
currency: CurrencyEnum.POINTS,
|
||||
additionalPrice: 0,
|
||||
additionalPriceCurrency: CurrencyEnum.POINTS,
|
||||
},
|
||||
requested: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
it("returns price and additionalPrice for single room with room features", () => {
|
||||
const nights = 2
|
||||
const result = getRedemptionPrice(
|
||||
@@ -1352,48 +1267,6 @@ describe("getVoucherPrice", () => {
|
||||
requested: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
it("does not return price for room without voucher", () => {
|
||||
const nights = 3
|
||||
const result = getVoucherPrice(
|
||||
[
|
||||
{
|
||||
adults: 1,
|
||||
breakfast: false,
|
||||
roomFeatures: [],
|
||||
roomRate: {
|
||||
redemption: {
|
||||
localPrice: {
|
||||
pointsPerStay: 150,
|
||||
currency: CurrencyEnum.POINTS,
|
||||
additionalPricePerStay: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
adults: 1,
|
||||
breakfast: false,
|
||||
roomFeatures: [],
|
||||
roomRate: {
|
||||
voucher: {
|
||||
numberOfVouchers: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
nights
|
||||
)
|
||||
|
||||
expect(result).toEqual({
|
||||
local: {
|
||||
price: 2,
|
||||
currency: CurrencyEnum.Voucher,
|
||||
additionalPrice: 0,
|
||||
},
|
||||
requested: undefined,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("getRegularPrice", () => {
|
||||
@@ -1412,34 +1285,6 @@ describe("getRegularPrice", () => {
|
||||
})
|
||||
})
|
||||
|
||||
it("returns price 0 for rooms without public or member rate", () => {
|
||||
const isMember = false
|
||||
const nights = 1
|
||||
const guest = { join: false }
|
||||
const result = getRegularPrice(
|
||||
[
|
||||
{
|
||||
adults: 1,
|
||||
breakfast: false,
|
||||
guest,
|
||||
roomFeatures: [],
|
||||
roomRate: {},
|
||||
},
|
||||
],
|
||||
isMember,
|
||||
nights
|
||||
)
|
||||
|
||||
expect(result).toEqual({
|
||||
local: {
|
||||
currency: CurrencyEnum.Unknown,
|
||||
price: 0,
|
||||
regularPrice: 0,
|
||||
},
|
||||
requested: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
it("calculates regular price for non-member", () => {
|
||||
const isMember = false
|
||||
const nights = 2
|
||||
@@ -1455,6 +1300,7 @@ describe("getRegularPrice", () => {
|
||||
public: {
|
||||
localPrice: {
|
||||
pricePerNight: 100,
|
||||
pricePerStay: 0,
|
||||
regularPricePerStay: 100,
|
||||
currency: CurrencyEnum.SEK,
|
||||
},
|
||||
@@ -1462,6 +1308,7 @@ describe("getRegularPrice", () => {
|
||||
member: {
|
||||
localPrice: {
|
||||
pricePerNight: 50,
|
||||
pricePerStay: 0,
|
||||
regularPricePerStay: 50,
|
||||
currency: CurrencyEnum.SEK,
|
||||
},
|
||||
@@ -1498,6 +1345,7 @@ describe("getRegularPrice", () => {
|
||||
public: {
|
||||
localPrice: {
|
||||
pricePerNight: 100,
|
||||
pricePerStay: 0,
|
||||
regularPricePerStay: 100,
|
||||
currency: CurrencyEnum.SEK,
|
||||
},
|
||||
@@ -1505,6 +1353,7 @@ describe("getRegularPrice", () => {
|
||||
member: {
|
||||
localPrice: {
|
||||
pricePerNight: 50,
|
||||
pricePerStay: 0,
|
||||
regularPricePerStay: 50,
|
||||
currency: CurrencyEnum.SEK,
|
||||
},
|
||||
@@ -1541,10 +1390,12 @@ describe("getRegularPrice", () => {
|
||||
member: {
|
||||
localPrice: {
|
||||
pricePerNight: 50,
|
||||
pricePerStay: 0,
|
||||
regularPricePerStay: 50,
|
||||
currency: CurrencyEnum.SEK,
|
||||
},
|
||||
},
|
||||
public: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1577,10 +1428,12 @@ describe("getRegularPrice", () => {
|
||||
public: {
|
||||
localPrice: {
|
||||
pricePerNight: 50,
|
||||
pricePerStay: 0,
|
||||
regularPricePerStay: 50,
|
||||
currency: CurrencyEnum.SEK,
|
||||
},
|
||||
},
|
||||
member: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1613,6 +1466,7 @@ describe("getRegularPrice", () => {
|
||||
public: {
|
||||
localPrice: {
|
||||
pricePerNight: 100,
|
||||
pricePerStay: 0,
|
||||
regularPricePerStay: 100,
|
||||
currency: CurrencyEnum.SEK,
|
||||
},
|
||||
@@ -1620,6 +1474,7 @@ describe("getRegularPrice", () => {
|
||||
member: {
|
||||
localPrice: {
|
||||
pricePerNight: 50,
|
||||
pricePerStay: 0,
|
||||
regularPricePerStay: 50,
|
||||
currency: CurrencyEnum.SEK,
|
||||
},
|
||||
@@ -1665,6 +1520,7 @@ describe("getRegularPrice", () => {
|
||||
public: {
|
||||
localPrice: {
|
||||
pricePerNight: 100,
|
||||
pricePerStay: 0,
|
||||
regularPricePerStay: 100,
|
||||
currency: CurrencyEnum.SEK,
|
||||
},
|
||||
@@ -1672,6 +1528,7 @@ describe("getRegularPrice", () => {
|
||||
member: {
|
||||
localPrice: {
|
||||
pricePerNight: 50,
|
||||
pricePerStay: 0,
|
||||
regularPricePerStay: 50,
|
||||
currency: CurrencyEnum.SEK,
|
||||
},
|
||||
@@ -1713,10 +1570,12 @@ describe("getRegularPrice", () => {
|
||||
public: {
|
||||
localPrice: {
|
||||
pricePerNight: 0,
|
||||
pricePerStay: 0,
|
||||
regularPricePerStay: 0,
|
||||
currency: CurrencyEnum.SEK,
|
||||
},
|
||||
},
|
||||
member: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1757,15 +1616,16 @@ describe("getRegularPrice", () => {
|
||||
public: {
|
||||
localPrice: {
|
||||
pricePerNight: 100,
|
||||
pricePerStay: 0,
|
||||
regularPricePerStay: 100,
|
||||
currency: CurrencyEnum.SEK,
|
||||
},
|
||||
requestedPrice: {
|
||||
currency: CurrencyEnum.EUR,
|
||||
pricePerNight: 10,
|
||||
regularPricePerStay: 10,
|
||||
pricePerStay: 0,
|
||||
},
|
||||
},
|
||||
member: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1800,6 +1660,7 @@ describe("getRegularPrice", () => {
|
||||
public: {
|
||||
localPrice: {
|
||||
pricePerNight: 100,
|
||||
pricePerStay: 0,
|
||||
regularPricePerStay: 100,
|
||||
currency: CurrencyEnum.SEK,
|
||||
},
|
||||
@@ -1807,6 +1668,7 @@ describe("getRegularPrice", () => {
|
||||
member: {
|
||||
localPrice: {
|
||||
pricePerNight: 50,
|
||||
pricePerStay: 0,
|
||||
regularPricePerStay: 50,
|
||||
currency: CurrencyEnum.SEK,
|
||||
},
|
||||
@@ -1822,6 +1684,7 @@ describe("getRegularPrice", () => {
|
||||
public: {
|
||||
localPrice: {
|
||||
pricePerNight: 75,
|
||||
pricePerStay: 0,
|
||||
regularPricePerStay: 75,
|
||||
currency: CurrencyEnum.SEK,
|
||||
},
|
||||
@@ -1829,6 +1692,7 @@ describe("getRegularPrice", () => {
|
||||
member: {
|
||||
localPrice: {
|
||||
pricePerNight: 30,
|
||||
pricePerStay: 0,
|
||||
regularPricePerStay: 30,
|
||||
currency: CurrencyEnum.SEK,
|
||||
},
|
||||
@@ -1849,6 +1713,36 @@ describe("getRegularPrice", () => {
|
||||
requested: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
it("returns unknown price for room without public and member rate", () => {
|
||||
const isMember = false
|
||||
const nights = 1
|
||||
const result = getRegularPrice(
|
||||
[
|
||||
{
|
||||
adults: 1,
|
||||
breakfast: false,
|
||||
roomFeatures: [],
|
||||
guest: { join: false },
|
||||
roomRate: {
|
||||
public: null,
|
||||
member: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
isMember,
|
||||
nights
|
||||
)
|
||||
|
||||
expect(result).toEqual({
|
||||
local: {
|
||||
currency: CurrencyEnum.Unknown,
|
||||
price: 0,
|
||||
regularPrice: 0,
|
||||
},
|
||||
requested: undefined,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
type TotalPriceRooms = Parameters<typeof getTotalPrice>[0]
|
||||
@@ -1868,15 +1762,8 @@ describe("getTotalPrice", () => {
|
||||
numberOfCheques: 3,
|
||||
additionalPricePerStay: 0,
|
||||
},
|
||||
rateCode: {} as never,
|
||||
rateType: {} as never,
|
||||
},
|
||||
bookingCode: {} as never,
|
||||
rateDefinition: {} as never,
|
||||
rate: {} as never,
|
||||
rateDefinitionMember: {} as never,
|
||||
},
|
||||
guest: {} as never,
|
||||
},
|
||||
{
|
||||
adults: 1,
|
||||
@@ -1886,33 +1773,21 @@ describe("getTotalPrice", () => {
|
||||
public: {
|
||||
localPrice: {
|
||||
pricePerStay: 500,
|
||||
pricePerNight: 500,
|
||||
regularPricePerStay: 500,
|
||||
currency: CurrencyEnum.SEK,
|
||||
omnibusPricePerNight: {} as never,
|
||||
pricePerNight: {} as never,
|
||||
regularPricePerNight: {} as never,
|
||||
regularPricePerStay: {} as never,
|
||||
},
|
||||
rateCode: {} as never,
|
||||
rateType: {} as never,
|
||||
},
|
||||
member: {
|
||||
localPrice: {
|
||||
pricePerNight: 30,
|
||||
pricePerStay: 30,
|
||||
regularPricePerStay: 30,
|
||||
currency: CurrencyEnum.SEK,
|
||||
omnibusPricePerNight: {} as never,
|
||||
pricePerStay: {} as never,
|
||||
regularPricePerNight: {} as never,
|
||||
},
|
||||
rateCode: {} as never,
|
||||
rateType: {} as never,
|
||||
},
|
||||
bookingCode: {} as never,
|
||||
rateDefinition: {} as never,
|
||||
rate: {} as never,
|
||||
rateDefinitionMember: {} as never,
|
||||
},
|
||||
guest: {} as never,
|
||||
guest: { join: false },
|
||||
},
|
||||
]
|
||||
|
||||
@@ -1941,20 +1816,11 @@ describe("getTotalPrice", () => {
|
||||
redemption: {
|
||||
localPrice: {
|
||||
pointsPerStay: 100,
|
||||
pointsPerNight: 100,
|
||||
currency: CurrencyEnum.POINTS,
|
||||
additionalPricePerStay: 0,
|
||||
},
|
||||
hasEnoughPoints: {} as never,
|
||||
rateCode: {} as never,
|
||||
rateType: {} as never,
|
||||
},
|
||||
bookingCode: {} as never,
|
||||
rateDefinition: {} as never,
|
||||
rate: {} as never,
|
||||
rateDefinitionMember: {} as never,
|
||||
},
|
||||
guest: {} as never,
|
||||
},
|
||||
{
|
||||
adults: 1,
|
||||
@@ -1964,33 +1830,21 @@ describe("getTotalPrice", () => {
|
||||
public: {
|
||||
localPrice: {
|
||||
pricePerStay: 500,
|
||||
pricePerNight: 500,
|
||||
regularPricePerStay: 500,
|
||||
currency: CurrencyEnum.SEK,
|
||||
omnibusPricePerNight: {} as never,
|
||||
pricePerNight: {} as never,
|
||||
regularPricePerNight: {} as never,
|
||||
regularPricePerStay: {} as never,
|
||||
},
|
||||
rateCode: {} as never,
|
||||
rateType: {} as never,
|
||||
},
|
||||
member: {
|
||||
localPrice: {
|
||||
pricePerNight: 30,
|
||||
pricePerStay: 30,
|
||||
regularPricePerStay: 30,
|
||||
currency: CurrencyEnum.SEK,
|
||||
omnibusPricePerNight: {} as never,
|
||||
pricePerStay: {} as never,
|
||||
regularPricePerNight: {} as never,
|
||||
},
|
||||
rateCode: {} as never,
|
||||
rateType: {} as never,
|
||||
},
|
||||
bookingCode: {} as never,
|
||||
rateDefinition: {} as never,
|
||||
rate: {} as never,
|
||||
rateDefinitionMember: {} as never,
|
||||
},
|
||||
guest: {} as never,
|
||||
guest: { join: false },
|
||||
},
|
||||
]
|
||||
|
||||
@@ -2018,15 +1872,8 @@ describe("getTotalPrice", () => {
|
||||
roomRate: {
|
||||
voucher: {
|
||||
numberOfVouchers: 1,
|
||||
rateCode: {} as never,
|
||||
rateType: {} as never,
|
||||
},
|
||||
bookingCode: {} as never,
|
||||
rateDefinition: {} as never,
|
||||
rate: {} as never,
|
||||
rateDefinitionMember: {} as never,
|
||||
},
|
||||
guest: {} as never,
|
||||
},
|
||||
{
|
||||
adults: 1,
|
||||
@@ -2036,33 +1883,21 @@ describe("getTotalPrice", () => {
|
||||
public: {
|
||||
localPrice: {
|
||||
pricePerStay: 500,
|
||||
pricePerNight: 500,
|
||||
regularPricePerStay: 500,
|
||||
currency: CurrencyEnum.SEK,
|
||||
omnibusPricePerNight: {} as never,
|
||||
pricePerNight: {} as never,
|
||||
regularPricePerNight: {} as never,
|
||||
regularPricePerStay: {} as never,
|
||||
},
|
||||
rateCode: {} as never,
|
||||
rateType: {} as never,
|
||||
},
|
||||
member: {
|
||||
localPrice: {
|
||||
pricePerNight: 30,
|
||||
pricePerStay: 30,
|
||||
regularPricePerStay: 30,
|
||||
currency: CurrencyEnum.SEK,
|
||||
omnibusPricePerNight: {} as never,
|
||||
pricePerStay: {} as never,
|
||||
regularPricePerNight: {} as never,
|
||||
},
|
||||
rateCode: {} as never,
|
||||
rateType: {} as never,
|
||||
},
|
||||
bookingCode: {} as never,
|
||||
rateDefinition: {} as never,
|
||||
rate: {} as never,
|
||||
rateDefinitionMember: {} as never,
|
||||
},
|
||||
guest: {} as never,
|
||||
guest: { join: false },
|
||||
},
|
||||
]
|
||||
|
||||
@@ -2092,31 +1927,19 @@ describe("getTotalPrice", () => {
|
||||
pricePerStay: 500,
|
||||
currency: CurrencyEnum.SEK,
|
||||
regularPricePerStay: 500,
|
||||
omnibusPricePerNight: {} as never,
|
||||
pricePerNight: {} as never,
|
||||
regularPricePerNight: {} as never,
|
||||
pricePerNight: 500,
|
||||
},
|
||||
rateCode: {} as never,
|
||||
rateType: {} as never,
|
||||
},
|
||||
member: {
|
||||
localPrice: {
|
||||
pricePerStay: 30,
|
||||
currency: CurrencyEnum.SEK,
|
||||
regularPricePerStay: 30,
|
||||
omnibusPricePerNight: {} as never,
|
||||
pricePerNight: {} as never,
|
||||
regularPricePerNight: {} as never,
|
||||
pricePerNight: 30,
|
||||
},
|
||||
rateCode: {} as never,
|
||||
rateType: {} as never,
|
||||
},
|
||||
bookingCode: {} as never,
|
||||
rateDefinition: {} as never,
|
||||
rate: {} as never,
|
||||
rateDefinitionMember: {} as never,
|
||||
},
|
||||
guest: { join: false } as never,
|
||||
guest: { join: false },
|
||||
},
|
||||
]
|
||||
|
||||
@@ -2131,4 +1954,64 @@ describe("getTotalPrice", () => {
|
||||
requested: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
it("returns regular price for room with only public rate", () => {
|
||||
const nights = 1
|
||||
const isMember = false
|
||||
const rooms: TotalPriceRooms = [
|
||||
{
|
||||
adults: 1,
|
||||
breakfast: false,
|
||||
roomFeatures: [],
|
||||
roomRate: {
|
||||
public: {
|
||||
localPrice: {
|
||||
pricePerStay: 500,
|
||||
currency: CurrencyEnum.SEK,
|
||||
regularPricePerStay: 500,
|
||||
pricePerNight: 500,
|
||||
},
|
||||
},
|
||||
member: null,
|
||||
},
|
||||
guest: { join: false },
|
||||
},
|
||||
]
|
||||
|
||||
const totalPrice = getTotalPrice(rooms, isMember, nights)
|
||||
|
||||
expect(totalPrice).toEqual({
|
||||
local: {
|
||||
price: 500,
|
||||
regularPrice: 0,
|
||||
currency: CurrencyEnum.SEK,
|
||||
},
|
||||
requested: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
it("returns unknown price for unknown room type", () => {
|
||||
const nights = 1
|
||||
const isMember = false
|
||||
const rooms: TotalPriceRooms = [
|
||||
{
|
||||
adults: 1,
|
||||
breakfast: false,
|
||||
roomFeatures: [],
|
||||
roomRate: {},
|
||||
guest: { join: false },
|
||||
},
|
||||
]
|
||||
|
||||
const totalPrice = getTotalPrice(rooms, isMember, nights)
|
||||
|
||||
expect(totalPrice).toEqual({
|
||||
local: {
|
||||
price: 0,
|
||||
currency: CurrencyEnum.Unknown,
|
||||
regularPrice: 0,
|
||||
},
|
||||
requested: undefined,
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,626 @@
|
||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||
import { RateTypeEnum } from "@scandic-hotels/common/constants/rateType"
|
||||
import { logger } from "@scandic-hotels/common/logger"
|
||||
|
||||
import { calculateRegularPrice } from "../../utils/calculateRegularPrice"
|
||||
import { sumPackages, sumPackagesRequestedPrice } from "../../utils/SelectRate"
|
||||
|
||||
import type { Product } from "@scandic-hotels/trpc/types/roomAvailability"
|
||||
|
||||
import type { Price } from "../../types/price"
|
||||
|
||||
function add(...nums: (number | string | undefined)[]) {
|
||||
return nums.reduce((total: number, num) => {
|
||||
if (typeof num === "undefined") {
|
||||
num = 0
|
||||
}
|
||||
total = total + parseInt(`${num}`)
|
||||
return total
|
||||
}, 0)
|
||||
}
|
||||
|
||||
export function getRoomPrice(
|
||||
roomRate: Product,
|
||||
isMember: boolean,
|
||||
pointsCurrency?: CurrencyEnum
|
||||
) {
|
||||
if (isMember && "member" in roomRate && roomRate.member) {
|
||||
let publicRate
|
||||
if (
|
||||
"public" in roomRate &&
|
||||
roomRate.public?.rateType === RateTypeEnum.Regular
|
||||
) {
|
||||
publicRate = roomRate.public
|
||||
}
|
||||
return {
|
||||
perNight: {
|
||||
requested: roomRate.member.requestedPrice
|
||||
? {
|
||||
currency: roomRate.member.requestedPrice.currency,
|
||||
price: roomRate.member.requestedPrice.pricePerNight,
|
||||
}
|
||||
: undefined,
|
||||
local: {
|
||||
currency: roomRate.member.localPrice.currency,
|
||||
price: roomRate.member.localPrice.pricePerNight,
|
||||
regularPrice:
|
||||
publicRate?.localPrice.pricePerStay ||
|
||||
roomRate.member.localPrice.regularPricePerNight,
|
||||
},
|
||||
},
|
||||
perStay: {
|
||||
requested: roomRate.member.requestedPrice
|
||||
? {
|
||||
currency: roomRate.member.requestedPrice.currency,
|
||||
price: roomRate.member.requestedPrice.pricePerStay,
|
||||
}
|
||||
: undefined,
|
||||
local: {
|
||||
currency: roomRate.member.localPrice.currency,
|
||||
price: roomRate.member.localPrice.pricePerStay,
|
||||
regularPrice:
|
||||
publicRate?.localPrice.pricePerStay ||
|
||||
roomRate.member.localPrice.regularPricePerStay,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if ("public" in roomRate && roomRate.public) {
|
||||
return {
|
||||
perNight: {
|
||||
requested: roomRate.public.requestedPrice
|
||||
? {
|
||||
currency: roomRate.public.requestedPrice.currency,
|
||||
price: roomRate.public.requestedPrice.pricePerNight,
|
||||
}
|
||||
: undefined,
|
||||
local: {
|
||||
currency: roomRate.public.localPrice.currency,
|
||||
price: roomRate.public.localPrice.pricePerNight,
|
||||
regularPrice: roomRate.public.localPrice.regularPricePerNight,
|
||||
},
|
||||
},
|
||||
perStay: {
|
||||
requested: roomRate.public.requestedPrice
|
||||
? {
|
||||
currency: roomRate.public.requestedPrice.currency,
|
||||
price: roomRate.public.requestedPrice.pricePerStay,
|
||||
}
|
||||
: undefined,
|
||||
local: {
|
||||
currency: roomRate.public.localPrice.currency,
|
||||
price: roomRate.public.localPrice.pricePerStay,
|
||||
regularPrice: roomRate.public.localPrice.regularPricePerStay,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if ("corporateCheque" in roomRate) {
|
||||
return {
|
||||
perNight: {
|
||||
requested: roomRate.corporateCheque.requestedPrice
|
||||
? {
|
||||
currency: CurrencyEnum.CC,
|
||||
price: roomRate.corporateCheque.requestedPrice.numberOfCheques,
|
||||
additionalPrice:
|
||||
roomRate.corporateCheque.requestedPrice.additionalPricePerStay,
|
||||
additionalPriceCurrency:
|
||||
roomRate.corporateCheque.requestedPrice.currency ?? undefined,
|
||||
}
|
||||
: undefined,
|
||||
local: {
|
||||
currency: CurrencyEnum.CC,
|
||||
price: roomRate.corporateCheque.localPrice.numberOfCheques,
|
||||
additionalPrice:
|
||||
roomRate.corporateCheque.localPrice.additionalPricePerStay,
|
||||
additionalPriceCurrency:
|
||||
roomRate.corporateCheque.localPrice.currency ?? undefined,
|
||||
},
|
||||
},
|
||||
perStay: {
|
||||
requested: roomRate.corporateCheque.requestedPrice
|
||||
? {
|
||||
currency: CurrencyEnum.CC,
|
||||
price: roomRate.corporateCheque.requestedPrice.numberOfCheques,
|
||||
additionalPrice:
|
||||
roomRate.corporateCheque.requestedPrice.additionalPricePerStay,
|
||||
additionalPriceCurrency:
|
||||
roomRate.corporateCheque.requestedPrice.currency ?? undefined,
|
||||
}
|
||||
: undefined,
|
||||
local: {
|
||||
currency: CurrencyEnum.CC,
|
||||
price: roomRate.corporateCheque.localPrice.numberOfCheques,
|
||||
additionalPrice:
|
||||
roomRate.corporateCheque.localPrice.additionalPricePerStay,
|
||||
additionalPriceCurrency:
|
||||
roomRate.corporateCheque.localPrice.currency ?? undefined,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if ("voucher" in roomRate) {
|
||||
return {
|
||||
perNight: {
|
||||
requested: undefined,
|
||||
local: {
|
||||
currency: CurrencyEnum.Voucher,
|
||||
price: roomRate.voucher.numberOfVouchers,
|
||||
},
|
||||
},
|
||||
perStay: {
|
||||
requested: undefined,
|
||||
local: {
|
||||
currency: CurrencyEnum.Voucher,
|
||||
price: roomRate.voucher.numberOfVouchers,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if ("redemption" in roomRate) {
|
||||
return {
|
||||
// ToDo Handle perNight as undefined
|
||||
perNight: {
|
||||
requested: undefined,
|
||||
local: {
|
||||
currency: pointsCurrency ?? CurrencyEnum.POINTS,
|
||||
price: roomRate.redemption.localPrice.pointsPerStay,
|
||||
additionalPrice:
|
||||
roomRate.redemption.localPrice.additionalPricePerStay,
|
||||
additionalPriceCurrency:
|
||||
roomRate.redemption.localPrice.currency ?? undefined,
|
||||
},
|
||||
},
|
||||
perStay: {
|
||||
requested: undefined,
|
||||
local: {
|
||||
currency: pointsCurrency ?? CurrencyEnum.POINTS,
|
||||
price: roomRate.redemption.localPrice.pointsPerStay,
|
||||
additionalPrice:
|
||||
roomRate.redemption.localPrice.additionalPricePerStay,
|
||||
additionalPriceCurrency:
|
||||
roomRate.redemption.localPrice.currency ?? undefined,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Unable to calculate RoomPrice since user is neither a member or memberRate is missing, or publicRate is missing`
|
||||
)
|
||||
}
|
||||
|
||||
export function getAdditionalPrice(
|
||||
total: {
|
||||
local: {
|
||||
additionalPrice?: number
|
||||
additionalPriceCurrency?: CurrencyEnum
|
||||
}
|
||||
},
|
||||
adults: number,
|
||||
breakfast:
|
||||
| { localPrice: { price: number; currency?: CurrencyEnum } }
|
||||
| false
|
||||
| undefined,
|
||||
nights: number,
|
||||
packages:
|
||||
| { localPrice: { totalPrice: number; currency?: CurrencyEnum } }[]
|
||||
| null
|
||||
| undefined,
|
||||
additionalPrice = 0,
|
||||
additionalPriceCurrency?: CurrencyEnum | null | undefined
|
||||
) {
|
||||
const breakfastLocalPrice =
|
||||
(breakfast ? breakfast.localPrice.price : 0) * nights * adults
|
||||
const pkgsSum = sumPackages(packages || [])
|
||||
|
||||
total.local.additionalPrice = add(
|
||||
total.local.additionalPrice,
|
||||
additionalPrice,
|
||||
breakfastLocalPrice,
|
||||
pkgsSum.price
|
||||
)
|
||||
|
||||
if (!total.local.additionalPriceCurrency) {
|
||||
if (additionalPriceCurrency) {
|
||||
total.local.additionalPriceCurrency = additionalPriceCurrency
|
||||
} else if (breakfast && breakfast.localPrice.currency) {
|
||||
total.local.additionalPriceCurrency = breakfast.localPrice.currency
|
||||
} else if (pkgsSum.currency) {
|
||||
total.local.additionalPriceCurrency = pkgsSum.currency
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getRequestedAdditionalPrice(
|
||||
total: Price,
|
||||
adults: number,
|
||||
breakfast:
|
||||
| {
|
||||
requestedPrice?: { price: number; currency?: CurrencyEnum }
|
||||
}
|
||||
| false
|
||||
| undefined,
|
||||
nights: number,
|
||||
packages:
|
||||
| { requestedPrice: { totalPrice: number; currency?: CurrencyEnum } }[]
|
||||
| null
|
||||
| undefined,
|
||||
cheques: number,
|
||||
additionalPrice = 0,
|
||||
additionalPriceCurrency: CurrencyEnum | null | undefined
|
||||
) {
|
||||
if (!total.requested) {
|
||||
total.requested = {
|
||||
currency: CurrencyEnum.CC,
|
||||
price: 0,
|
||||
}
|
||||
}
|
||||
|
||||
total.requested.price = add(total.requested.price, cheques)
|
||||
|
||||
const breakfastRequestedPrice =
|
||||
(breakfast ? breakfast.requestedPrice?.price || 0 : 0) * nights * adults
|
||||
const pkgsSumRequested = sumPackagesRequestedPrice(packages)
|
||||
|
||||
total.requested.additionalPrice = add(
|
||||
total.requested.additionalPrice,
|
||||
additionalPrice,
|
||||
breakfastRequestedPrice,
|
||||
pkgsSumRequested.price
|
||||
)
|
||||
|
||||
if (!total.requested.additionalPriceCurrency) {
|
||||
if (additionalPriceCurrency) {
|
||||
total.requested.additionalPriceCurrency = additionalPriceCurrency
|
||||
} else if (pkgsSumRequested.currency) {
|
||||
total.requested.additionalPriceCurrency = pkgsSumRequested.currency
|
||||
} else if (breakfast && breakfast.requestedPrice) {
|
||||
total.requested.additionalPriceCurrency =
|
||||
breakfast.requestedPrice.currency
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type BasePriceCalculationRoom = {
|
||||
adults: number
|
||||
breakfast:
|
||||
| {
|
||||
localPrice: { price: number; currency?: CurrencyEnum }
|
||||
requestedPrice?: { price: number; currency?: CurrencyEnum }
|
||||
}
|
||||
| false
|
||||
| undefined
|
||||
roomFeatures:
|
||||
| {
|
||||
localPrice: { totalPrice: number; currency?: CurrencyEnum }
|
||||
requestedPrice: { totalPrice: number; currency?: CurrencyEnum }
|
||||
}[]
|
||||
| null
|
||||
| undefined
|
||||
// We don't care about what type of roomRate it is yet
|
||||
roomRate: object
|
||||
}
|
||||
|
||||
type CorporateCheckRoom = BasePriceCalculationRoom & {
|
||||
roomRate: {
|
||||
corporateCheque: {
|
||||
localPrice: {
|
||||
numberOfCheques: number
|
||||
additionalPricePerStay: number
|
||||
currency?: CurrencyEnum
|
||||
}
|
||||
requestedPrice?: {
|
||||
numberOfCheques: number
|
||||
additionalPricePerStay: number
|
||||
currency?: CurrencyEnum
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
export function getCorporateChequePrice(
|
||||
rooms: CorporateCheckRoom[],
|
||||
nights: number
|
||||
) {
|
||||
return rooms.reduce<Price>(
|
||||
(total, room) => {
|
||||
const corporateCheque = room.roomRate.corporateCheque
|
||||
|
||||
total.local.price = add(
|
||||
total.local.price,
|
||||
corporateCheque.localPrice.numberOfCheques
|
||||
)
|
||||
|
||||
getAdditionalPrice(
|
||||
total,
|
||||
room.adults,
|
||||
room.breakfast,
|
||||
nights,
|
||||
room.roomFeatures,
|
||||
corporateCheque.localPrice.additionalPricePerStay,
|
||||
corporateCheque.localPrice.currency
|
||||
)
|
||||
|
||||
if (corporateCheque.requestedPrice) {
|
||||
getRequestedAdditionalPrice(
|
||||
total,
|
||||
room.adults,
|
||||
room.breakfast,
|
||||
nights,
|
||||
room.roomFeatures,
|
||||
corporateCheque.requestedPrice.numberOfCheques,
|
||||
corporateCheque.requestedPrice?.additionalPricePerStay,
|
||||
corporateCheque.requestedPrice?.currency
|
||||
)
|
||||
}
|
||||
|
||||
return total
|
||||
},
|
||||
{
|
||||
local: {
|
||||
currency: CurrencyEnum.CC,
|
||||
price: 0,
|
||||
},
|
||||
requested: undefined,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
type VoucherRoom = BasePriceCalculationRoom & {
|
||||
roomRate: {
|
||||
voucher: {
|
||||
numberOfVouchers: number
|
||||
}
|
||||
}
|
||||
}
|
||||
export function getVoucherPrice(rooms: VoucherRoom[], nights: number) {
|
||||
return rooms.reduce<Price>(
|
||||
(total, room) => {
|
||||
const voucher = room.roomRate.voucher
|
||||
|
||||
total.local.price = add(total.local.price, voucher.numberOfVouchers)
|
||||
|
||||
getAdditionalPrice(
|
||||
total,
|
||||
room.adults,
|
||||
room.breakfast,
|
||||
nights,
|
||||
room.roomFeatures
|
||||
)
|
||||
|
||||
return total
|
||||
},
|
||||
{
|
||||
local: {
|
||||
currency: CurrencyEnum.Voucher,
|
||||
price: 0,
|
||||
},
|
||||
requested: undefined,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
type RedemptionRoom = BasePriceCalculationRoom & {
|
||||
roomRate: {
|
||||
redemption: {
|
||||
localPrice: {
|
||||
pointsPerStay: number
|
||||
additionalPricePerStay: number
|
||||
currency?: CurrencyEnum
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getRedemptionPrice(
|
||||
rooms: RedemptionRoom[],
|
||||
nights: number,
|
||||
pointsCurrency?: CurrencyEnum
|
||||
) {
|
||||
return rooms.reduce<Price>(
|
||||
(total, room) => {
|
||||
const redemption = room.roomRate.redemption
|
||||
|
||||
total.local.price = add(
|
||||
total.local.price,
|
||||
redemption.localPrice.pointsPerStay
|
||||
)
|
||||
|
||||
getAdditionalPrice(
|
||||
total,
|
||||
room.adults,
|
||||
room.breakfast,
|
||||
nights,
|
||||
room.roomFeatures,
|
||||
redemption.localPrice.additionalPricePerStay,
|
||||
redemption.localPrice.currency
|
||||
)
|
||||
|
||||
return total
|
||||
},
|
||||
{
|
||||
local: {
|
||||
currency: pointsCurrency ?? CurrencyEnum.POINTS,
|
||||
price: 0,
|
||||
},
|
||||
requested: undefined,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
type RegularPriceCalculationRoom = BasePriceCalculationRoom & {
|
||||
guest: {
|
||||
join: boolean
|
||||
membershipNo?: string | null
|
||||
}
|
||||
roomRate: {
|
||||
member?: {
|
||||
localPrice: RegularRoomLocalPrice
|
||||
requestedPrice?: RegularRoomRequestedPrice | null
|
||||
} | null
|
||||
public?: {
|
||||
localPrice: RegularRoomLocalPrice
|
||||
requestedPrice?: RegularRoomRequestedPrice | null
|
||||
} | null
|
||||
}
|
||||
}
|
||||
type RegularRoomLocalPrice = {
|
||||
currency: CurrencyEnum
|
||||
pricePerStay: number
|
||||
pricePerNight: number
|
||||
regularPricePerStay: number
|
||||
}
|
||||
type RegularRoomRequestedPrice = {
|
||||
currency: CurrencyEnum
|
||||
pricePerStay: number
|
||||
}
|
||||
|
||||
export function getRegularPrice(
|
||||
rooms: RegularPriceCalculationRoom[],
|
||||
isMember: boolean,
|
||||
nights: number
|
||||
) {
|
||||
const totalPrice = rooms.reduce<Price>(
|
||||
(total, room, idx) => {
|
||||
const isMainRoomAndMember = idx === 0 && isMember
|
||||
const join = Boolean(room.guest.join || room.guest.membershipNo)
|
||||
const memberRate = room.roomRate.member
|
||||
const publicRate = room.roomRate.public
|
||||
const useMemberRate = (isMainRoomAndMember || join) && memberRate
|
||||
|
||||
const rate = useMemberRate ? memberRate : publicRate
|
||||
|
||||
if (!rate) {
|
||||
return total
|
||||
}
|
||||
|
||||
const breakfastLocalPrice =
|
||||
(room.breakfast ? room.breakfast.localPrice.price || 0 : 0) *
|
||||
nights *
|
||||
room.adults
|
||||
const pkgsSum = sumPackages(room.roomFeatures || [])
|
||||
const additionalCost = breakfastLocalPrice + pkgsSum.price
|
||||
|
||||
total.local.currency = rate.localPrice.currency
|
||||
total.local.price = add(
|
||||
total.local.price,
|
||||
rate.localPrice.pricePerStay,
|
||||
additionalCost
|
||||
)
|
||||
|
||||
if (rate.requestedPrice) {
|
||||
if (!total.requested) {
|
||||
total.requested = {
|
||||
currency: rate.requestedPrice.currency,
|
||||
price: 0,
|
||||
}
|
||||
}
|
||||
|
||||
const breakfastRequestedPrice =
|
||||
(room.breakfast ? (room.breakfast.requestedPrice?.price ?? 0) : 0) *
|
||||
nights *
|
||||
room.adults
|
||||
const pkgsSumRequested = sumPackagesRequestedPrice(room.roomFeatures)
|
||||
|
||||
total.requested.price = add(
|
||||
total.requested.price,
|
||||
rate.requestedPrice.pricePerStay,
|
||||
breakfastRequestedPrice,
|
||||
pkgsSumRequested.price
|
||||
)
|
||||
}
|
||||
|
||||
return calculateRegularPrice({
|
||||
total,
|
||||
useMemberRate: !!useMemberRate,
|
||||
regularMemberPrice: memberRate
|
||||
? {
|
||||
pricePerStay: memberRate.localPrice.pricePerNight,
|
||||
regularPricePerStay: memberRate.localPrice.regularPricePerStay,
|
||||
}
|
||||
: undefined,
|
||||
regularPublicPrice: publicRate
|
||||
? {
|
||||
pricePerStay: publicRate.localPrice.pricePerNight,
|
||||
regularPricePerStay: publicRate.localPrice.regularPricePerStay,
|
||||
}
|
||||
: undefined,
|
||||
additionalCost,
|
||||
})
|
||||
},
|
||||
{
|
||||
local: {
|
||||
currency: CurrencyEnum.Unknown,
|
||||
price: 0,
|
||||
regularPrice: 0,
|
||||
},
|
||||
requested: undefined,
|
||||
}
|
||||
)
|
||||
|
||||
if (
|
||||
totalPrice.local.regularPrice &&
|
||||
totalPrice.local.price >= totalPrice.local.regularPrice
|
||||
) {
|
||||
totalPrice.local.regularPrice = 0
|
||||
}
|
||||
|
||||
return totalPrice
|
||||
}
|
||||
|
||||
export function getTotalPrice(
|
||||
rooms: (
|
||||
| RegularPriceCalculationRoom
|
||||
| CorporateCheckRoom
|
||||
| RedemptionRoom
|
||||
| VoucherRoom
|
||||
)[],
|
||||
isMember: boolean,
|
||||
nights: number,
|
||||
pointsCurrency?: CurrencyEnum
|
||||
) {
|
||||
const corporateChequeRooms = rooms.filter(
|
||||
(x): x is CorporateCheckRoom => "corporateCheque" in x.roomRate
|
||||
)
|
||||
if (corporateChequeRooms.length > 0) {
|
||||
return getCorporateChequePrice(corporateChequeRooms, nights)
|
||||
}
|
||||
|
||||
const redemptionRooms = rooms.filter(
|
||||
(x): x is RedemptionRoom => "redemption" in x.roomRate
|
||||
)
|
||||
if (redemptionRooms.length > 0) {
|
||||
return getRedemptionPrice(redemptionRooms, nights, pointsCurrency)
|
||||
}
|
||||
|
||||
const voucherRooms = rooms.filter(
|
||||
(x): x is VoucherRoom => "voucher" in x.roomRate
|
||||
)
|
||||
if (voucherRooms.length > 0) {
|
||||
return getVoucherPrice(voucherRooms, nights)
|
||||
}
|
||||
|
||||
const regularRooms = rooms.filter(
|
||||
(x): x is RegularPriceCalculationRoom =>
|
||||
"member" in x.roomRate || "public" in x.roomRate
|
||||
)
|
||||
if (regularRooms.length > 0) {
|
||||
return getRegularPrice(regularRooms, isMember, nights)
|
||||
}
|
||||
|
||||
logger.warn(
|
||||
"Unable to determine room type for price calculation, return zero price"
|
||||
)
|
||||
return {
|
||||
local: {
|
||||
currency: CurrencyEnum.Unknown,
|
||||
price: 0,
|
||||
regularPrice: 0,
|
||||
},
|
||||
requested: undefined,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user