Merged in chore/regular-price-tests (pull request #3075)

chore: Add tests for regular price calculations

* Add tests for calculateRegularPrice

* Add tests for getRegularPrice

* Add multiroom test


Approved-by: Joakim Jäderberg
Approved-by: Linus Flood
This commit is contained in:
Anton Gunnarsson
2025-11-10 08:04:52 +00:00
parent e08d24cc75
commit ba58ae6245
3 changed files with 639 additions and 6 deletions

View File

@@ -5,6 +5,7 @@ import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import {
getAdditionalPrice,
getRedemptionPrice,
getRegularPrice,
getVoucherPrice,
} from "./helpers"
@@ -312,6 +313,44 @@ describe("getRedemptionPrice", () => {
})
})
it("returns price and additionalPrice for single room with room features", () => {
const nights = 2
const result = getRedemptionPrice(
[
{
adults: 1,
breakfast: false,
roomFeatures: [
{
localPrice: { totalPrice: 33 },
requestedPrice: { totalPrice: 33 },
},
],
roomRate: {
redemption: {
localPrice: {
pointsPerStay: 100,
currency: CurrencyEnum.POINTS,
additionalPricePerStay: 0,
},
},
},
},
],
nights
)
expect(result).toEqual({
local: {
price: 100,
currency: CurrencyEnum.POINTS,
additionalPrice: 33,
additionalPriceCurrency: CurrencyEnum.POINTS,
},
requested: undefined,
})
})
describe("getVoucherPrice", () => {
it("returns price 0 and default currency when rooms are empty", () => {
const result = getVoucherPrice([], 1)
@@ -458,3 +497,458 @@ describe("getVoucherPrice", () => {
})
})
})
describe("getRegularPrice", () => {
it("returns price 0 for empty rooms", () => {
const isMember = false
const nights = 1
const result = getRegularPrice([], isMember, nights)
expect(result).toEqual({
local: {
currency: CurrencyEnum.Unknown,
price: 0,
regularPrice: 0,
},
requested: undefined,
})
})
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
const guest = { join: false }
const result = getRegularPrice(
[
{
adults: 1,
breakfast: false,
roomFeatures: null,
guest,
roomRate: {
public: {
localPrice: {
pricePerNight: 100,
regularPricePerStay: 100,
currency: CurrencyEnum.SEK,
},
},
member: {
localPrice: {
pricePerNight: 50,
regularPricePerStay: 50,
currency: CurrencyEnum.SEK,
},
},
},
},
],
isMember,
nights
)
expect(result).toEqual({
local: {
currency: CurrencyEnum.SEK,
price: 0,
regularPrice: 100,
},
requested: undefined,
})
})
it("calculates regular price for member", () => {
const isMember = true
const nights = 2
const guest = { join: false }
const result = getRegularPrice(
[
{
adults: 1,
breakfast: false,
roomFeatures: [],
guest,
roomRate: {
public: {
localPrice: {
pricePerNight: 100,
regularPricePerStay: 100,
currency: CurrencyEnum.SEK,
},
},
member: {
localPrice: {
pricePerNight: 50,
regularPricePerStay: 50,
currency: CurrencyEnum.SEK,
},
},
},
},
],
isMember,
nights
)
expect(result).toEqual({
local: {
currency: CurrencyEnum.SEK,
price: 0,
regularPrice: 100,
},
requested: undefined,
})
})
it("calculates regular price for member without public rate", () => {
const isMember = true
const nights = 2
const guest = { join: false }
const result = getRegularPrice(
[
{
adults: 1,
breakfast: false,
roomFeatures: [],
guest,
roomRate: {
member: {
localPrice: {
pricePerNight: 50,
regularPricePerStay: 50,
currency: CurrencyEnum.SEK,
},
},
},
},
],
isMember,
nights
)
expect(result).toEqual({
local: {
currency: CurrencyEnum.SEK,
price: 0,
regularPrice: 50,
},
requested: undefined,
})
})
it("calculates regular price for non-member without member rate", () => {
const isMember = false
const nights = 2
const guest = { join: false }
const result = getRegularPrice(
[
{
adults: 1,
breakfast: false,
roomFeatures: [],
guest,
roomRate: {
public: {
localPrice: {
pricePerNight: 50,
regularPricePerStay: 50,
currency: CurrencyEnum.SEK,
},
},
},
},
],
isMember,
nights
)
expect(result).toEqual({
local: {
currency: CurrencyEnum.SEK,
price: 0,
regularPrice: 50,
},
requested: undefined,
})
})
it("calculates regular price for non-member with breakfast", () => {
const isMember = false
const nights = 2
const guest = { join: false }
const result = getRegularPrice(
[
{
adults: 1,
breakfast: { localPrice: { price: 20, currency: CurrencyEnum.SEK } },
roomFeatures: [],
guest,
roomRate: {
public: {
localPrice: {
pricePerNight: 100,
regularPricePerStay: 100,
currency: CurrencyEnum.SEK,
},
},
member: {
localPrice: {
pricePerNight: 50,
regularPricePerStay: 50,
currency: CurrencyEnum.SEK,
},
},
},
},
],
isMember,
nights
)
expect(result).toEqual({
local: {
currency: CurrencyEnum.SEK,
price: 40,
regularPrice: 140,
},
requested: undefined,
})
})
it("calculates regular price for non-member with breakfast and room features", () => {
const isMember = false
const nights = 2
const guest = { join: false }
const result = getRegularPrice(
[
{
adults: 1,
breakfast: { localPrice: { price: 20, currency: CurrencyEnum.SEK } },
roomFeatures: [
{
localPrice: { totalPrice: 3, currency: CurrencyEnum.SEK },
requestedPrice: { totalPrice: 3, currency: CurrencyEnum.SEK },
},
{
localPrice: { totalPrice: 7, currency: CurrencyEnum.SEK },
requestedPrice: { totalPrice: 7, currency: CurrencyEnum.SEK },
},
],
guest,
roomRate: {
public: {
localPrice: {
pricePerNight: 100,
regularPricePerStay: 100,
currency: CurrencyEnum.SEK,
},
},
member: {
localPrice: {
pricePerNight: 50,
regularPricePerStay: 50,
currency: CurrencyEnum.SEK,
},
},
},
},
],
isMember,
nights
)
expect(result).toEqual({
local: {
currency: CurrencyEnum.SEK,
price: 50,
regularPrice: 150,
},
requested: undefined,
})
})
it("calculates regular price to 0 when price is same or larger than regular price", () => {
const isMember = false
const nights = 2
const guest = { join: false }
const result = getRegularPrice(
[
{
adults: 1,
breakfast: { localPrice: { price: 0, currency: CurrencyEnum.SEK } },
roomFeatures: [
{
localPrice: { totalPrice: 100, currency: CurrencyEnum.SEK },
requestedPrice: { totalPrice: 3, currency: CurrencyEnum.SEK },
},
],
guest,
roomRate: {
public: {
localPrice: {
pricePerNight: 0,
regularPricePerStay: 0,
currency: CurrencyEnum.SEK,
},
},
},
},
],
isMember,
nights
)
expect(result).toEqual({
local: {
currency: CurrencyEnum.SEK,
price: 100,
regularPrice: 0,
},
requested: undefined,
})
})
it("calculates requested price with breakfast and room features", () => {
const isMember = false
const nights = 2
const guest = { join: false }
const result = getRegularPrice(
[
{
adults: 1,
breakfast: {
localPrice: { price: 30, currency: CurrencyEnum.SEK },
requestedPrice: { price: 3, currency: CurrencyEnum.EUR },
},
roomFeatures: [
{
localPrice: { totalPrice: 20, currency: CurrencyEnum.SEK },
requestedPrice: { totalPrice: 2, currency: CurrencyEnum.EUR },
},
],
guest,
roomRate: {
public: {
localPrice: {
pricePerNight: 100,
regularPricePerStay: 100,
currency: CurrencyEnum.SEK,
},
requestedPrice: {
currency: CurrencyEnum.EUR,
pricePerNight: 10,
regularPricePerStay: 10,
},
},
},
},
],
isMember,
nights
)
expect(result).toEqual({
local: {
currency: CurrencyEnum.SEK,
price: 80,
regularPrice: 180,
},
requested: {
currency: CurrencyEnum.EUR,
price: 8,
},
})
})
it("calculates prices for multi-room", () => {
const isMember = true
const nights = 2
const result = getRegularPrice(
[
{
adults: 1,
breakfast: { localPrice: { price: 20, currency: CurrencyEnum.SEK } },
roomFeatures: null,
guest: { join: false },
roomRate: {
public: {
localPrice: {
pricePerNight: 100,
regularPricePerStay: 100,
currency: CurrencyEnum.SEK,
},
},
member: {
localPrice: {
pricePerNight: 50,
regularPricePerStay: 50,
currency: CurrencyEnum.SEK,
},
},
},
},
{
adults: 1,
breakfast: { localPrice: { price: 25, currency: CurrencyEnum.SEK } },
roomFeatures: null,
guest: { join: true },
roomRate: {
public: {
localPrice: {
pricePerNight: 75,
regularPricePerStay: 75,
currency: CurrencyEnum.SEK,
},
},
member: {
localPrice: {
pricePerNight: 30,
regularPricePerStay: 30,
currency: CurrencyEnum.SEK,
},
},
},
},
],
isMember,
nights
)
expect(result).toEqual({
local: {
currency: CurrencyEnum.SEK,
price: 90,
regularPrice: 265,
},
requested: undefined,
})
})
})

View File

@@ -12,7 +12,6 @@ import type { BreakfastPackage } from "@scandic-hotels/trpc/routers/hotels/schem
import type { Packages } from "@scandic-hotels/trpc/types/packages"
import type {
CorporateChequeProduct,
PriceProduct,
Product,
} from "@scandic-hotels/trpc/types/roomAvailability"
import type { User } from "@scandic-hotels/trpc/types/user"
@@ -456,12 +455,16 @@ export function getVoucherPrice(rooms: PriceCalculationRoom[], nights: number) {
type PriceCalculationRoom = {
adults: number
breakfast:
| { localPrice: { price: number; currency?: CurrencyEnum } }
| {
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
@@ -518,14 +521,45 @@ export function getRedemptionPrice(
)
}
interface TRoomPriceProduct extends TRoom {
roomRate: PriceProduct
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
}
}
}
function getRegularPrice(rooms: TRoom[], isMember: boolean, nights: number) {
export function getRegularPrice(
rooms: RegularPriceCalculationRoom[],
isMember: boolean,
nights: number
) {
const totalPrice = rooms
.filter(
(room): room is TRoomPriceProduct =>
(room): room is GetRegularPriceRoom =>
"member" in room.roomRate || "public" in room.roomRate
)
.reduce<Price>(

View File

@@ -0,0 +1,105 @@
import { describe, expect, it } from "vitest"
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import { calculateRegularPrice } from "./calculateRegularPrice"
describe("calculateRegularPrice", () => {
it("returns total if useMemberRate is true and missing regularMemberPrice", () => {
const result = calculateRegularPrice({
total: {
local: { price: 1, currency: CurrencyEnum.SEK },
},
regularMemberPrice: undefined,
regularPublicPrice: { pricePerStay: 10 },
useMemberRate: true,
})
expect(result).toEqual({
local: { price: 1, currency: CurrencyEnum.SEK },
})
})
it("returns total if useMemberRate is false and missing regularPublicPrice", () => {
const result = calculateRegularPrice({
total: {
local: { price: 1, currency: CurrencyEnum.SEK },
},
regularMemberPrice: { pricePerStay: 10 },
regularPublicPrice: undefined,
useMemberRate: false,
})
expect(result).toEqual({
local: { price: 1, currency: CurrencyEnum.SEK },
})
})
it("calculates regularPrice for regularPublicPrice", () => {
const result = calculateRegularPrice({
total: {
local: { price: 1, currency: CurrencyEnum.SEK },
},
regularMemberPrice: undefined,
regularPublicPrice: { pricePerStay: 10 },
useMemberRate: false,
})
expect(result).toEqual({
local: { price: 1, regularPrice: 10, currency: CurrencyEnum.SEK },
})
})
it("calculates regularPrice for regularPublicPrice with regularPricePerStay", () => {
const result = calculateRegularPrice({
total: {
local: { price: 1, currency: CurrencyEnum.SEK },
},
regularMemberPrice: undefined,
regularPublicPrice: { pricePerStay: 10, regularPricePerStay: 20 },
useMemberRate: false,
})
expect(result).toEqual({
local: { price: 1, regularPrice: 20, currency: CurrencyEnum.SEK },
})
})
it("calculates regularPrice for member with regularMemberPrice", () => {
const result = calculateRegularPrice({
total: {
local: { price: 1, currency: CurrencyEnum.SEK },
},
regularMemberPrice: { pricePerStay: 10 },
regularPublicPrice: undefined,
useMemberRate: true,
})
expect(result).toEqual({
local: { price: 1, regularPrice: 10, currency: CurrencyEnum.SEK },
})
})
it("calculates regularPrice for member with regularPublicPrice and regularMemberRate", () => {
const result = calculateRegularPrice({
total: {
local: { price: 1, currency: CurrencyEnum.SEK },
},
regularMemberPrice: { pricePerStay: 15 },
regularPublicPrice: { pricePerStay: 10 },
useMemberRate: true,
})
expect(result).toEqual({
local: { price: 1, regularPrice: 10, currency: CurrencyEnum.SEK },
})
})
it("calculates regularPrice for member with regularMemberRate with pricePerStay", () => {
const result = calculateRegularPrice({
total: {
local: { price: 1, currency: CurrencyEnum.SEK },
},
regularMemberPrice: { pricePerStay: 15, regularPricePerStay: 20 },
regularPublicPrice: undefined,
useMemberRate: true,
})
expect(result).toEqual({
local: { price: 1, regularPrice: 20, currency: CurrencyEnum.SEK },
})
})
})