From 7fc49428c74772421abc8b55aa52974c03235a40 Mon Sep 17 00:00:00 2001 From: Anton Gunnarsson Date: Tue, 4 Nov 2025 12:50:51 +0000 Subject: [PATCH] Merged in chore/price-calculation-tests (pull request #3072) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore: Add more price calculation tests * Add tests and refactor types for getVoucherPrice * Add tests for sumPackagesRequestedPrice * Add tests for calculateVat Approved-by: Joakim Jäderberg --- .../lib/stores/enter-details/helpers.test.ts | 153 +++++++++++++++++- .../lib/stores/enter-details/helpers.ts | 19 ++- .../lib/utils/SelectRate/index.test.ts | 46 +++++- .../lib/utils/SelectRate/index.tsx | 8 +- 4 files changed, 214 insertions(+), 12 deletions(-) diff --git a/packages/booking-flow/lib/stores/enter-details/helpers.test.ts b/packages/booking-flow/lib/stores/enter-details/helpers.test.ts index 5ad8a8995..963af0d43 100644 --- a/packages/booking-flow/lib/stores/enter-details/helpers.test.ts +++ b/packages/booking-flow/lib/stores/enter-details/helpers.test.ts @@ -2,7 +2,11 @@ import { describe, expect, it } from "vitest" import { CurrencyEnum } from "@scandic-hotels/common/constants/currency" -import { getAdditionalPrice, getRedemptionPrice } from "./helpers" +import { + getAdditionalPrice, + getRedemptionPrice, + getVoucherPrice, +} from "./helpers" type GetAdditionalPriceParams = Parameters describe("getAdditionalPrice", () => { @@ -307,3 +311,150 @@ describe("getRedemptionPrice", () => { }) }) }) + +describe("getVoucherPrice", () => { + it("returns price 0 and default currency when rooms are empty", () => { + const result = getVoucherPrice([], 1) + + expect(result).toEqual({ + local: { price: 0, currency: CurrencyEnum.Voucher }, + requested: undefined, + }) + }) + + it("returns price for single room with voucher price", () => { + const nights = 1 + const result = getVoucherPrice( + [ + { + adults: 1, + breakfast: false, + roomFeatures: [], + roomRate: { + voucher: { + numberOfVouchers: 1, + }, + }, + }, + ], + nights + ) + + expect(result).toEqual({ + local: { + price: 1, + currency: CurrencyEnum.Voucher, + additionalPrice: 0, + }, + requested: undefined, + }) + }) + + it("returns price for single room with multiple nights", () => { + const nights = 3 + const result = getVoucherPrice( + [ + { + adults: 1, + breakfast: false, + roomFeatures: [], + roomRate: { + voucher: { + numberOfVouchers: 3, + }, + }, + }, + ], + nights + ) + + expect(result).toEqual({ + local: { + price: 3, + currency: CurrencyEnum.Voucher, + additionalPrice: 0, + }, + requested: undefined, + }) + }) + + it("returns price for multiple rooms with multiple nights", () => { + const nights = 3 + const result = getVoucherPrice( + [ + { + adults: 1, + breakfast: false, + roomFeatures: [], + roomRate: { + voucher: { + numberOfVouchers: 3, + }, + }, + }, + { + adults: 1, + breakfast: false, + roomFeatures: [], + roomRate: { + voucher: { + numberOfVouchers: 2, + }, + }, + }, + ], + nights + ) + + expect(result).toEqual({ + local: { + price: 5, + currency: CurrencyEnum.Voucher, + additionalPrice: 0, + }, + 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, + }) + }) +}) diff --git a/packages/booking-flow/lib/stores/enter-details/helpers.ts b/packages/booking-flow/lib/stores/enter-details/helpers.ts index 7c804deab..ef4ff5e29 100644 --- a/packages/booking-flow/lib/stores/enter-details/helpers.ts +++ b/packages/booking-flow/lib/stores/enter-details/helpers.ts @@ -14,7 +14,6 @@ import type { CorporateChequeProduct, PriceProduct, Product, - VoucherProduct, } from "@scandic-hotels/trpc/types/roomAvailability" import type { User } from "@scandic-hotels/trpc/types/user" @@ -417,13 +416,17 @@ function getCorporateChequePrice(rooms: TRoom[], nights: number) { ) } -interface TRoomVoucher extends TRoom { - roomRate: VoucherProduct +type VoucherRoom = PriceCalculationRoom & { + roomRate: { + voucher: { + numberOfVouchers: number + } + } } -function getVoucherPrice(rooms: TRoom[], nights: number) { +export function getVoucherPrice(rooms: PriceCalculationRoom[], nights: number) { return rooms - .filter((room): room is TRoomVoucher => "voucher" in room.roomRate) + .filter((room): room is VoucherRoom => "voucher" in room.roomRate) .reduce( (total, room) => { const voucher = room.roomRate.voucher @@ -450,7 +453,7 @@ function getVoucherPrice(rooms: TRoom[], nights: number) { ) } -type GetRedemptionPriceRoom = { +type PriceCalculationRoom = { adults: number breakfast: | { localPrice: { price: number; currency?: CurrencyEnum } } @@ -465,7 +468,7 @@ type GetRedemptionPriceRoom = { // We don't care about roomRate unless it's RedemptionProduct roomRate: object } -type RedemptionRoom = GetRedemptionPriceRoom & { +type RedemptionRoom = PriceCalculationRoom & { roomRate: { redemption: { localPrice: { @@ -478,7 +481,7 @@ type RedemptionRoom = GetRedemptionPriceRoom & { } export function getRedemptionPrice( - rooms: GetRedemptionPriceRoom[], + rooms: PriceCalculationRoom[], nights: number, pointsCurrency?: CurrencyEnum ) { diff --git a/packages/booking-flow/lib/utils/SelectRate/index.test.ts b/packages/booking-flow/lib/utils/SelectRate/index.test.ts index 99d827b5e..cc30c7b11 100644 --- a/packages/booking-flow/lib/utils/SelectRate/index.test.ts +++ b/packages/booking-flow/lib/utils/SelectRate/index.test.ts @@ -4,7 +4,12 @@ import { AlertTypeEnum } from "@scandic-hotels/common/constants/alert" import { CurrencyEnum } from "@scandic-hotels/common/constants/currency" import { dt } from "@scandic-hotels/common/dt" -import { filterOverlappingDates, sumPackages } from "./index" +import { + calculateVat, + filterOverlappingDates, + sumPackages, + sumPackagesRequestedPrice, +} from "./index" import type { specialAlertsSchema } from "@scandic-hotels/trpc/routers/hotels/schemas/hotel/specialAlerts" import type { z } from "zod" @@ -98,3 +103,42 @@ describe("sumPackages", () => { expect(result).toEqual({ currency: CurrencyEnum.SEK, price: 350 }) }) }) + +describe("sumPackagesRequestedPrice", () => { + it("returns 0 price for null packages", () => { + const result = sumPackagesRequestedPrice(null) + expect(result).toEqual({ currency: undefined, price: 0 }) + }) + + it("returns 0 price for undefined packages", () => { + const result = sumPackagesRequestedPrice(undefined) + expect(result).toEqual({ currency: undefined, price: 0 }) + }) + + it("returns 0 price for empty packages", () => { + const result = sumPackagesRequestedPrice([]) + expect(result).toEqual({ currency: undefined, price: 0 }) + }) + + it("sums prices of packages", () => { + const result = sumPackagesRequestedPrice([ + { requestedPrice: { totalPrice: 100, currency: CurrencyEnum.SEK } }, + { requestedPrice: { totalPrice: 200, currency: CurrencyEnum.SEK } }, + { requestedPrice: { totalPrice: 50, currency: CurrencyEnum.SEK } }, + ]) + expect(result).toEqual({ currency: CurrencyEnum.SEK, price: 350 }) + }) +}) + +describe("calculateVat", () => { + it("calculates VAT correctly", () => { + expect(calculateVat(100, 25)).toEqual({ + priceExclVat: 80, + vatAmount: 20, + }) + expect(calculateVat(5, 100)).toEqual({ + priceExclVat: 2.5, + vatAmount: 2.5, + }) + }) +}) diff --git a/packages/booking-flow/lib/utils/SelectRate/index.tsx b/packages/booking-flow/lib/utils/SelectRate/index.tsx index 4db6d5035..8cd6b99ca 100644 --- a/packages/booking-flow/lib/utils/SelectRate/index.tsx +++ b/packages/booking-flow/lib/utils/SelectRate/index.tsx @@ -9,7 +9,6 @@ import { ChildBedTypeEnum } from "@scandic-hotels/trpc/enums/childBedTypeEnum" import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter" import type { CurrencyEnum } from "@scandic-hotels/common/constants/currency" -import type { Packages } from "@scandic-hotels/trpc/types/packages" import type { JSX } from "react" import type { RoomPackageCodes } from "../../types/components/selectRate/roomFilter" @@ -63,7 +62,12 @@ export function sumPackages( ) } -export function sumPackagesRequestedPrice(packages: Packages | null) { +export function sumPackagesRequestedPrice( + packages: + | { requestedPrice: { totalPrice: number; currency?: CurrencyEnum } }[] + | null + | undefined +) { if (!packages || !packages.length) { return { currency: undefined,