diff --git a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/page.tsx b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/page.tsx index 37311010a..bd81ddfa7 100644 --- a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/page.tsx +++ b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/page.tsx @@ -96,7 +96,7 @@ export default async function DetailsPage({ hotel.merchantInformationData.alternatePaymentOptions = [] } - const { hotelsTrackingData, pageTrackingData } = getTracking( + const { hotelsTrackingData, pageTrackingData, ancillaries } = getTracking( booking, hotel, rooms, @@ -170,7 +170,11 @@ export default async function DetailsPage({ - + ) } diff --git a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/tracking.ts b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/tracking.ts index ebeef6b18..6eb4719d0 100644 --- a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/tracking.ts +++ b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/tracking.ts @@ -2,18 +2,25 @@ import { differenceInCalendarDays, format, isWeekend } from "date-fns" import { REDEMPTION } from "@/constants/booking" +import { sumPackages } from "@/components/HotelReservation/utils" import { getSpecialRoomType } from "@/utils/specialRoomType" import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums" +import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter" import type { SelectRateSearchParams } from "@/types/components/hotelReservation/selectRate/selectRate" import { TrackingChannelEnum, + type TrackingSDKAncillaries, type TrackingSDKHotelInfo, type TrackingSDKPageData, } from "@/types/components/tracking" import { CurrencyEnum } from "@/types/enums/currency" import type { Hotel } from "@/types/hotel" import type { Room } from "@/types/providers/details/room" +import type { + PriceProduct, + Product, +} from "@/types/trpc/routers/hotel/roomAvailability" import type { Lang } from "@/constants/languages" import type { SelectHotelParams } from "@/utils/url" @@ -47,6 +54,10 @@ export function getTracking( .join("|"), analyticsRateCode: rooms.map((room) => room.rate).join("|"), arrivalDate: format(arrivalDate, "yyyy-MM-dd"), + // only include bedtype here if it is "preselected" meanining there is only 1 + bedType: rooms + .map((r) => (r?.bedTypes?.length === 1 ? r.bedTypes[0].description : "-")) + .join("|"), // Comma separated booking code values in "code,code,n/a" format for multiroom and "code" or "n/a" for singleroom // n/a is used whenever code is Not applicable as defined by Tracking team bookingCode: rooms @@ -72,6 +83,22 @@ export function getTracking( .join("|"), country: hotel?.address.country, departureDate: format(departureDate, "yyyy-MM-dd"), + discount: rooms + .map((room, idx) => { + const isMainRoom = idx === 0 + if ( + hasMemberPrice(room.roomRate) && + isMainRoom && + isMember && + hasPublicPrice(room.roomRate) + ) { + const memberPrice = room.roomRate.member.localPrice.pricePerStay + const publicPrice = room.roomRate.public.localPrice.pricePerStay + return publicPrice - memberPrice + } + return 0 + }) + .join(","), duration: differenceInCalendarDays(departureDate, arrivalDate), hotelID: hotel?.operaId, leadTime: differenceInCalendarDays(arrivalDate, new Date()), @@ -80,17 +107,28 @@ export function getTracking( .map((room) => room.childrenInRoom?.length ?? 0) .join(","), noOfRooms: booking.rooms.length, + ...(rooms.length > 1 && { + multiroomRateIdentity: rooms.every((room) => { + const firstRoom = rooms[0] + if ( + hasPublicPrice(firstRoom.roomRate) && + hasPublicPrice(room.roomRate) + ) { + return ( + firstRoom.roomRate.public?.localPrice.pricePerNight === + room.roomRate.public?.localPrice.pricePerNight + ) + } + }) + ? "same rate" + : "mixed rate", + }), rateCode: rooms .map((room, idx) => { const isMainRoom = idx === 0 - if ( - "member" in room.roomRate && - room.roomRate.member && - isMember && - isMainRoom - ) { + if (hasMemberPrice(room.roomRate) && isMember && isMainRoom) { return room.roomRate.member.rateCode - } else if ("public" in room.roomRate && room.roomRate.public) { + } else if (hasPublicPrice(room.roomRate)) { return room.roomRate.public.rateCode } else if ("corporateCheque" in room.roomRate) { return room.roomRate.corporateCheque.rateCode @@ -108,25 +146,29 @@ export function getTracking( rateCodeName: rooms.map((room) => room.rateDefinitionTitle).join(","), rateCodeType: rooms.map((room) => room.rateType.toLowerCase()).join(","), region: hotel?.address.city, - revenueCurrencyCode: rooms - .map((room) => { - if ("corporateCheque" in room.roomRate) { - return CurrencyEnum.CC - } else if ("redemption" in room.roomRate) { - return CurrencyEnum.POINTS - } else if ("voucher" in room.roomRate) { - return CurrencyEnum.Voucher - } else if ("public" in room.roomRate && room.roomRate.public) { - return room.roomRate.public.localPrice.currency - } else if ("member" in room.roomRate && room.roomRate.member) { - return room.roomRate.member.localPrice.currency - } - return CurrencyEnum.Unknown - }) - .join(","), + revenueCurrencyCode: [ + ...new Set( + rooms.map((room) => { + if ("corporateCheque" in room.roomRate) { + return CurrencyEnum.CC + } else if ("redemption" in room.roomRate) { + return CurrencyEnum.POINTS + } else if ("voucher" in room.roomRate) { + return CurrencyEnum.Voucher + } else if (hasPublicPrice(room.roomRate)) { + return room.roomRate.public.localPrice.currency + } else if (hasMemberPrice(room.roomRate)) { + return room.roomRate.member.localPrice.currency + } + return CurrencyEnum.Unknown + }) + ), + ].join(","), rewardNight: booking.searchType === REDEMPTION ? "yes" : "no", rewardNightAvailability: booking.searchType === REDEMPTION ? "true" : "false", + roomPrice: calcTotalRoomPrice(rooms, isMember), + totalPrice: calcTotalPrice(rooms, isMember), searchTerm: city, searchType: "hotel", specialRoomType: rooms @@ -134,8 +176,91 @@ export function getTracking( .join(","), } + const roomsWithPetRoom = rooms.filter(hasPetRoom) + const ancillaries: TrackingSDKAncillaries = roomsWithPetRoom + .slice(0, 1) // should only be one item + .map((room) => { + const petRoomPackage = room.packages.find( + (p) => p.code === RoomPackageCodeEnum.PET_ROOM + ) + return { + hotelId: hotel.operaId, + productId: petRoomPackage?.code!, // property is guaranteed at this point + productUnits: roomsWithPetRoom.length, + productPoints: 0, + productPrice: petRoomPackage?.localPrice.totalPrice ?? 0, + productType: "room preference", + productName: "pet room", + productCategory: "", + } + }) + return { hotelsTrackingData, pageTrackingData, + ancillaries, } } + +function hasPublicPrice( + roomRate: Product +): roomRate is PriceProduct & { public: NonNullable } { + if ("public" in roomRate && roomRate.public) { + return true + } + return false +} + +function hasMemberPrice( + roomRate: Product +): roomRate is PriceProduct & { member: NonNullable } { + if ("member" in roomRate && roomRate.member) { + return true + } + return false +} + +function hasPetRoom( + room: Room +): room is Room & { packages: NonNullable } { + if (!room.packages) { + return false + } + return room.packages.some((p) => p.code === RoomPackageCodeEnum.PET_ROOM) +} + +function calcTotalPrice(rooms: Room[], isMember: boolean) { + const totalRoomPrice = calcTotalRoomPrice(rooms, isMember) + const totalPackageSum = rooms.reduce((total, room) => { + const packageSum = sumPackages(room.packages) + return (total += packageSum.price ?? 0) + }, 0) + + return totalRoomPrice + totalPackageSum +} + +function calcTotalRoomPrice(rooms: Room[], isMember: boolean) { + return rooms.reduce((total, room, idx) => { + // When it comes special rates, only redemption has additional price and that should be added + // other special rates like voucher, corporateCheck should be added as 0 according to Priyanka + if ("redemption" in room.roomRate) { + return ( + room.roomRate.redemption.requestedPrice?.additionalPricePerStay ?? 0 + ) + } else if ( + "corporateCheck" in room.roomRate || + "voucher" in room.roomRate + ) { + return 0 + } + + const isMainRoom = idx === 0 + if (hasMemberPrice(room.roomRate) && isMember && isMainRoom) { + total += room.roomRate.member.localPrice.pricePerStay + } else if (hasPublicPrice(room.roomRate)) { + total += room.roomRate.public.localPrice.pricePerStay + } + + return total + }, 0) +} diff --git a/apps/scandic-web/components/HotelReservation/BookingConfirmation/Tracking/tracking.ts b/apps/scandic-web/components/HotelReservation/BookingConfirmation/Tracking/tracking.ts index 4333eee13..00f016ef8 100644 --- a/apps/scandic-web/components/HotelReservation/BookingConfirmation/Tracking/tracking.ts +++ b/apps/scandic-web/components/HotelReservation/BookingConfirmation/Tracking/tracking.ts @@ -146,17 +146,19 @@ export function getTracking( .toLowerCase(), //rateCodeType: , //TODO: Add when available in API. "regular, promotion, corporate etx", region: hotel?.address.city, - revenueCurrencyCode: rooms.map((r) => r.currencyCode).join(","), + revenueCurrencyCode: [...new Set(rooms.map((r) => r.currencyCode))].join( + "," + ), rewardNight: booking.roomPoints > 0 ? "yes" : "no", rewardNightAvailability: booking.roomPoints > 0 ? "true" : "false", points: booking.roomPoints > 0 ? booking.roomPoints : undefined, - roomPrice: rooms.map((r) => r.roomPrice).join(","), + roomPrice: rooms.reduce((total, room) => total + room.roomPrice, 0), roomTypeCode: rooms.map((r) => r.roomTypeCode ?? "-").join(","), searchType: "hotel", specialRoomType: rooms .map((room) => getSpecialRoomType(room.packages)) .join(","), - totalPrice: rooms.map((r) => r.totalPrice).join(","), + totalPrice: rooms.reduce((total, room) => total + room.totalPrice, 0), lateArrivalGuarantee: booking.rateDefinition.mustBeGuaranteed ? "mandatory" : isFlexBooking diff --git a/apps/scandic-web/types/components/tracking.ts b/apps/scandic-web/types/components/tracking.ts index 47eea8b05..c89dc9341 100644 --- a/apps/scandic-web/types/components/tracking.ts +++ b/apps/scandic-web/types/components/tracking.ts @@ -70,11 +70,12 @@ export type TrackingSDKHotelInfo = { childBedPreference?: string country?: string // Country of the hotel departureDate?: string - discount?: number + discount?: number | string duration?: number // Number of nights to stay hotelID?: string leadTime?: number // Number of days from booking date until arrivalDate lowestRoomPrice?: number + multiroomRateIdentity?: string //modifyValues?: string // ,roomtype:value>,bed: noOfAdults?: number | string // multiroom support, "2,1,3" noOfChildren?: number | string // multiroom support, "2,1,3"