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:
Anton Gunnarsson
2025-11-27 13:44:45 +00:00
parent 5ac7e5deac
commit 27b3f41bff
5 changed files with 755 additions and 865 deletions

View File

@@ -16,10 +16,10 @@ import {
import { EnterDetailsStepEnum } from "../../stores/enter-details/enterDetailsStep" import { EnterDetailsStepEnum } from "../../stores/enter-details/enterDetailsStep"
import { import {
clearSessionStorage, clearSessionStorage,
getTotalPrice,
readFromSessionStorage, readFromSessionStorage,
writeToSessionStorage, writeToSessionStorage,
} from "../../stores/enter-details/helpers" } from "../../stores/enter-details/helpers"
import { getTotalPrice } from "../../stores/enter-details/priceCalculations"
import { isSameBooking } from "../../utils/isSameBooking" import { isSameBooking } from "../../utils/isSameBooking"
import type { Lang } from "@scandic-hotels/common/constants/language" import type { Lang } from "@scandic-hotels/common/constants/language"

View File

@@ -1,17 +1,11 @@
import { parsePhoneNumberFromString } from "libphonenumber-js" 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 { logger } from "@scandic-hotels/common/logger"
import { calculateRegularPrice } from "../../utils/calculateRegularPrice"
import { sumPackages, sumPackagesRequestedPrice } from "../../utils/SelectRate"
import { detailsStorageName } from "." import { detailsStorageName } from "."
import type { Product } from "@scandic-hotels/trpc/types/roomAvailability"
import type { User } from "@scandic-hotels/trpc/types/user" import type { User } from "@scandic-hotels/trpc/types/user"
import type { Price } from "../../types/price"
import type { PersistedState, RoomState } from "./types" import type { PersistedState, RoomState } from "./types"
export function extractGuestFromUser(user: User) { 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"]) => { export const checkRoomProgress = (steps: RoomState["steps"]) => {
return Object.values(steps) return Object.values(steps)
.filter(Boolean) .filter(Boolean)
@@ -267,430 +76,3 @@ export function clearSessionStorage() {
} }
sessionStorage.removeItem(detailsStorageName) 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)
}

View File

@@ -11,10 +11,9 @@ import { EnterDetailsStepEnum } from "./enterDetailsStep"
import { import {
checkRoomProgress, checkRoomProgress,
extractGuestFromUser, extractGuestFromUser,
getRoomPrice,
getTotalPrice,
writeToSessionStorage, writeToSessionStorage,
} from "./helpers" } from "./helpers"
import { getRoomPrice, getTotalPrice } from "./priceCalculations"
import type { CurrencyEnum } from "@scandic-hotels/common/constants/currency" import type { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import type { Lang } from "@scandic-hotels/common/constants/language" import type { Lang } from "@scandic-hotels/common/constants/language"

View File

@@ -11,7 +11,7 @@ import {
getRequestedAdditionalPrice, getRequestedAdditionalPrice,
getTotalPrice, getTotalPrice,
getVoucherPrice, getVoucherPrice,
} from "./helpers" } from "./priceCalculations"
type GetAdditionalPriceParams = Parameters<typeof getAdditionalPrice> type GetAdditionalPriceParams = Parameters<typeof getAdditionalPrice>
describe("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", () => { it("calculates combined price with breakfast and room features", () => {
const nights = 3 const nights = 3
const rooms: GetCorporateChequePriceParams[0] = [ 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", () => { it("returns price and additionalPrice for single room with room features", () => {
const nights = 2 const nights = 2
const result = getRedemptionPrice( const result = getRedemptionPrice(
@@ -1352,48 +1267,6 @@ describe("getVoucherPrice", () => {
requested: undefined, 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", () => { 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", () => { it("calculates regular price for non-member", () => {
const isMember = false const isMember = false
const nights = 2 const nights = 2
@@ -1455,6 +1300,7 @@ describe("getRegularPrice", () => {
public: { public: {
localPrice: { localPrice: {
pricePerNight: 100, pricePerNight: 100,
pricePerStay: 0,
regularPricePerStay: 100, regularPricePerStay: 100,
currency: CurrencyEnum.SEK, currency: CurrencyEnum.SEK,
}, },
@@ -1462,6 +1308,7 @@ describe("getRegularPrice", () => {
member: { member: {
localPrice: { localPrice: {
pricePerNight: 50, pricePerNight: 50,
pricePerStay: 0,
regularPricePerStay: 50, regularPricePerStay: 50,
currency: CurrencyEnum.SEK, currency: CurrencyEnum.SEK,
}, },
@@ -1498,6 +1345,7 @@ describe("getRegularPrice", () => {
public: { public: {
localPrice: { localPrice: {
pricePerNight: 100, pricePerNight: 100,
pricePerStay: 0,
regularPricePerStay: 100, regularPricePerStay: 100,
currency: CurrencyEnum.SEK, currency: CurrencyEnum.SEK,
}, },
@@ -1505,6 +1353,7 @@ describe("getRegularPrice", () => {
member: { member: {
localPrice: { localPrice: {
pricePerNight: 50, pricePerNight: 50,
pricePerStay: 0,
regularPricePerStay: 50, regularPricePerStay: 50,
currency: CurrencyEnum.SEK, currency: CurrencyEnum.SEK,
}, },
@@ -1541,10 +1390,12 @@ describe("getRegularPrice", () => {
member: { member: {
localPrice: { localPrice: {
pricePerNight: 50, pricePerNight: 50,
pricePerStay: 0,
regularPricePerStay: 50, regularPricePerStay: 50,
currency: CurrencyEnum.SEK, currency: CurrencyEnum.SEK,
}, },
}, },
public: null,
}, },
}, },
], ],
@@ -1577,10 +1428,12 @@ describe("getRegularPrice", () => {
public: { public: {
localPrice: { localPrice: {
pricePerNight: 50, pricePerNight: 50,
pricePerStay: 0,
regularPricePerStay: 50, regularPricePerStay: 50,
currency: CurrencyEnum.SEK, currency: CurrencyEnum.SEK,
}, },
}, },
member: null,
}, },
}, },
], ],
@@ -1613,6 +1466,7 @@ describe("getRegularPrice", () => {
public: { public: {
localPrice: { localPrice: {
pricePerNight: 100, pricePerNight: 100,
pricePerStay: 0,
regularPricePerStay: 100, regularPricePerStay: 100,
currency: CurrencyEnum.SEK, currency: CurrencyEnum.SEK,
}, },
@@ -1620,6 +1474,7 @@ describe("getRegularPrice", () => {
member: { member: {
localPrice: { localPrice: {
pricePerNight: 50, pricePerNight: 50,
pricePerStay: 0,
regularPricePerStay: 50, regularPricePerStay: 50,
currency: CurrencyEnum.SEK, currency: CurrencyEnum.SEK,
}, },
@@ -1665,6 +1520,7 @@ describe("getRegularPrice", () => {
public: { public: {
localPrice: { localPrice: {
pricePerNight: 100, pricePerNight: 100,
pricePerStay: 0,
regularPricePerStay: 100, regularPricePerStay: 100,
currency: CurrencyEnum.SEK, currency: CurrencyEnum.SEK,
}, },
@@ -1672,6 +1528,7 @@ describe("getRegularPrice", () => {
member: { member: {
localPrice: { localPrice: {
pricePerNight: 50, pricePerNight: 50,
pricePerStay: 0,
regularPricePerStay: 50, regularPricePerStay: 50,
currency: CurrencyEnum.SEK, currency: CurrencyEnum.SEK,
}, },
@@ -1713,10 +1570,12 @@ describe("getRegularPrice", () => {
public: { public: {
localPrice: { localPrice: {
pricePerNight: 0, pricePerNight: 0,
pricePerStay: 0,
regularPricePerStay: 0, regularPricePerStay: 0,
currency: CurrencyEnum.SEK, currency: CurrencyEnum.SEK,
}, },
}, },
member: null,
}, },
}, },
], ],
@@ -1757,15 +1616,16 @@ describe("getRegularPrice", () => {
public: { public: {
localPrice: { localPrice: {
pricePerNight: 100, pricePerNight: 100,
pricePerStay: 0,
regularPricePerStay: 100, regularPricePerStay: 100,
currency: CurrencyEnum.SEK, currency: CurrencyEnum.SEK,
}, },
requestedPrice: { requestedPrice: {
currency: CurrencyEnum.EUR, currency: CurrencyEnum.EUR,
pricePerNight: 10, pricePerStay: 0,
regularPricePerStay: 10,
}, },
}, },
member: null,
}, },
}, },
], ],
@@ -1800,6 +1660,7 @@ describe("getRegularPrice", () => {
public: { public: {
localPrice: { localPrice: {
pricePerNight: 100, pricePerNight: 100,
pricePerStay: 0,
regularPricePerStay: 100, regularPricePerStay: 100,
currency: CurrencyEnum.SEK, currency: CurrencyEnum.SEK,
}, },
@@ -1807,6 +1668,7 @@ describe("getRegularPrice", () => {
member: { member: {
localPrice: { localPrice: {
pricePerNight: 50, pricePerNight: 50,
pricePerStay: 0,
regularPricePerStay: 50, regularPricePerStay: 50,
currency: CurrencyEnum.SEK, currency: CurrencyEnum.SEK,
}, },
@@ -1822,6 +1684,7 @@ describe("getRegularPrice", () => {
public: { public: {
localPrice: { localPrice: {
pricePerNight: 75, pricePerNight: 75,
pricePerStay: 0,
regularPricePerStay: 75, regularPricePerStay: 75,
currency: CurrencyEnum.SEK, currency: CurrencyEnum.SEK,
}, },
@@ -1829,6 +1692,7 @@ describe("getRegularPrice", () => {
member: { member: {
localPrice: { localPrice: {
pricePerNight: 30, pricePerNight: 30,
pricePerStay: 0,
regularPricePerStay: 30, regularPricePerStay: 30,
currency: CurrencyEnum.SEK, currency: CurrencyEnum.SEK,
}, },
@@ -1849,6 +1713,36 @@ describe("getRegularPrice", () => {
requested: undefined, 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] type TotalPriceRooms = Parameters<typeof getTotalPrice>[0]
@@ -1868,15 +1762,8 @@ describe("getTotalPrice", () => {
numberOfCheques: 3, numberOfCheques: 3,
additionalPricePerStay: 0, 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, adults: 1,
@@ -1886,33 +1773,21 @@ describe("getTotalPrice", () => {
public: { public: {
localPrice: { localPrice: {
pricePerStay: 500, pricePerStay: 500,
pricePerNight: 500,
regularPricePerStay: 500,
currency: CurrencyEnum.SEK, currency: CurrencyEnum.SEK,
omnibusPricePerNight: {} as never,
pricePerNight: {} as never,
regularPricePerNight: {} as never,
regularPricePerStay: {} as never,
}, },
rateCode: {} as never,
rateType: {} as never,
}, },
member: { member: {
localPrice: { localPrice: {
pricePerNight: 30, pricePerNight: 30,
pricePerStay: 30,
regularPricePerStay: 30, regularPricePerStay: 30,
currency: CurrencyEnum.SEK, 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: { redemption: {
localPrice: { localPrice: {
pointsPerStay: 100, pointsPerStay: 100,
pointsPerNight: 100,
currency: CurrencyEnum.POINTS, currency: CurrencyEnum.POINTS,
additionalPricePerStay: 0, 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, adults: 1,
@@ -1964,33 +1830,21 @@ describe("getTotalPrice", () => {
public: { public: {
localPrice: { localPrice: {
pricePerStay: 500, pricePerStay: 500,
pricePerNight: 500,
regularPricePerStay: 500,
currency: CurrencyEnum.SEK, currency: CurrencyEnum.SEK,
omnibusPricePerNight: {} as never,
pricePerNight: {} as never,
regularPricePerNight: {} as never,
regularPricePerStay: {} as never,
}, },
rateCode: {} as never,
rateType: {} as never,
}, },
member: { member: {
localPrice: { localPrice: {
pricePerNight: 30, pricePerNight: 30,
pricePerStay: 30,
regularPricePerStay: 30, regularPricePerStay: 30,
currency: CurrencyEnum.SEK, 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: { roomRate: {
voucher: { voucher: {
numberOfVouchers: 1, 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, adults: 1,
@@ -2036,33 +1883,21 @@ describe("getTotalPrice", () => {
public: { public: {
localPrice: { localPrice: {
pricePerStay: 500, pricePerStay: 500,
pricePerNight: 500,
regularPricePerStay: 500,
currency: CurrencyEnum.SEK, currency: CurrencyEnum.SEK,
omnibusPricePerNight: {} as never,
pricePerNight: {} as never,
regularPricePerNight: {} as never,
regularPricePerStay: {} as never,
}, },
rateCode: {} as never,
rateType: {} as never,
}, },
member: { member: {
localPrice: { localPrice: {
pricePerNight: 30, pricePerNight: 30,
pricePerStay: 30,
regularPricePerStay: 30, regularPricePerStay: 30,
currency: CurrencyEnum.SEK, 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, pricePerStay: 500,
currency: CurrencyEnum.SEK, currency: CurrencyEnum.SEK,
regularPricePerStay: 500, regularPricePerStay: 500,
omnibusPricePerNight: {} as never, pricePerNight: 500,
pricePerNight: {} as never,
regularPricePerNight: {} as never,
}, },
rateCode: {} as never,
rateType: {} as never,
}, },
member: { member: {
localPrice: { localPrice: {
pricePerStay: 30, pricePerStay: 30,
currency: CurrencyEnum.SEK, currency: CurrencyEnum.SEK,
regularPricePerStay: 30, regularPricePerStay: 30,
omnibusPricePerNight: {} as never, pricePerNight: 30,
pricePerNight: {} as never,
regularPricePerNight: {} as never,
}, },
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, 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,
})
})
}) })

View File

@@ -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,
}
}