Merged in fix/SW-1353-multiroom-tracking (pull request #1870)
fix(SW-1353): enter details tracking fixes * fix(SW-1353): tracking fixes * refactor: move code * fix: use hasPublicPrice * fix: update calculation for roomPrice and totalPrice * fix: only include bedtype if it is "preselected" Approved-by: Michael Zetterberg
This commit is contained in:
@@ -96,7 +96,7 @@ export default async function DetailsPage({
|
|||||||
hotel.merchantInformationData.alternatePaymentOptions = []
|
hotel.merchantInformationData.alternatePaymentOptions = []
|
||||||
}
|
}
|
||||||
|
|
||||||
const { hotelsTrackingData, pageTrackingData } = getTracking(
|
const { hotelsTrackingData, pageTrackingData, ancillaries } = getTracking(
|
||||||
booking,
|
booking,
|
||||||
hotel,
|
hotel,
|
||||||
rooms,
|
rooms,
|
||||||
@@ -170,7 +170,11 @@ export default async function DetailsPage({
|
|||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<TrackingSDK hotelInfo={hotelsTrackingData} pageData={pageTrackingData} />
|
<TrackingSDK
|
||||||
|
hotelInfo={hotelsTrackingData}
|
||||||
|
pageData={pageTrackingData}
|
||||||
|
ancillaries={ancillaries}
|
||||||
|
/>
|
||||||
</EnterDetailsProvider>
|
</EnterDetailsProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,18 +2,25 @@ import { differenceInCalendarDays, format, isWeekend } from "date-fns"
|
|||||||
|
|
||||||
import { REDEMPTION } from "@/constants/booking"
|
import { REDEMPTION } from "@/constants/booking"
|
||||||
|
|
||||||
|
import { sumPackages } from "@/components/HotelReservation/utils"
|
||||||
import { getSpecialRoomType } from "@/utils/specialRoomType"
|
import { getSpecialRoomType } from "@/utils/specialRoomType"
|
||||||
|
|
||||||
import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums"
|
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 type { SelectRateSearchParams } from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||||
import {
|
import {
|
||||||
TrackingChannelEnum,
|
TrackingChannelEnum,
|
||||||
|
type TrackingSDKAncillaries,
|
||||||
type TrackingSDKHotelInfo,
|
type TrackingSDKHotelInfo,
|
||||||
type TrackingSDKPageData,
|
type TrackingSDKPageData,
|
||||||
} from "@/types/components/tracking"
|
} from "@/types/components/tracking"
|
||||||
import { CurrencyEnum } from "@/types/enums/currency"
|
import { CurrencyEnum } from "@/types/enums/currency"
|
||||||
import type { Hotel } from "@/types/hotel"
|
import type { Hotel } from "@/types/hotel"
|
||||||
import type { Room } from "@/types/providers/details/room"
|
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 { Lang } from "@/constants/languages"
|
||||||
import type { SelectHotelParams } from "@/utils/url"
|
import type { SelectHotelParams } from "@/utils/url"
|
||||||
|
|
||||||
@@ -47,6 +54,10 @@ export function getTracking(
|
|||||||
.join("|"),
|
.join("|"),
|
||||||
analyticsRateCode: rooms.map((room) => room.rate).join("|"),
|
analyticsRateCode: rooms.map((room) => room.rate).join("|"),
|
||||||
arrivalDate: format(arrivalDate, "yyyy-MM-dd"),
|
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
|
// 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
|
// n/a is used whenever code is Not applicable as defined by Tracking team
|
||||||
bookingCode: rooms
|
bookingCode: rooms
|
||||||
@@ -72,6 +83,22 @@ export function getTracking(
|
|||||||
.join("|"),
|
.join("|"),
|
||||||
country: hotel?.address.country,
|
country: hotel?.address.country,
|
||||||
departureDate: format(departureDate, "yyyy-MM-dd"),
|
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),
|
duration: differenceInCalendarDays(departureDate, arrivalDate),
|
||||||
hotelID: hotel?.operaId,
|
hotelID: hotel?.operaId,
|
||||||
leadTime: differenceInCalendarDays(arrivalDate, new Date()),
|
leadTime: differenceInCalendarDays(arrivalDate, new Date()),
|
||||||
@@ -80,17 +107,28 @@ export function getTracking(
|
|||||||
.map((room) => room.childrenInRoom?.length ?? 0)
|
.map((room) => room.childrenInRoom?.length ?? 0)
|
||||||
.join(","),
|
.join(","),
|
||||||
noOfRooms: booking.rooms.length,
|
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
|
rateCode: rooms
|
||||||
.map((room, idx) => {
|
.map((room, idx) => {
|
||||||
const isMainRoom = idx === 0
|
const isMainRoom = idx === 0
|
||||||
if (
|
if (hasMemberPrice(room.roomRate) && isMember && isMainRoom) {
|
||||||
"member" in room.roomRate &&
|
|
||||||
room.roomRate.member &&
|
|
||||||
isMember &&
|
|
||||||
isMainRoom
|
|
||||||
) {
|
|
||||||
return room.roomRate.member.rateCode
|
return room.roomRate.member.rateCode
|
||||||
} else if ("public" in room.roomRate && room.roomRate.public) {
|
} else if (hasPublicPrice(room.roomRate)) {
|
||||||
return room.roomRate.public.rateCode
|
return room.roomRate.public.rateCode
|
||||||
} else if ("corporateCheque" in room.roomRate) {
|
} else if ("corporateCheque" in room.roomRate) {
|
||||||
return room.roomRate.corporateCheque.rateCode
|
return room.roomRate.corporateCheque.rateCode
|
||||||
@@ -108,25 +146,29 @@ export function getTracking(
|
|||||||
rateCodeName: rooms.map((room) => room.rateDefinitionTitle).join(","),
|
rateCodeName: rooms.map((room) => room.rateDefinitionTitle).join(","),
|
||||||
rateCodeType: rooms.map((room) => room.rateType.toLowerCase()).join(","),
|
rateCodeType: rooms.map((room) => room.rateType.toLowerCase()).join(","),
|
||||||
region: hotel?.address.city,
|
region: hotel?.address.city,
|
||||||
revenueCurrencyCode: rooms
|
revenueCurrencyCode: [
|
||||||
.map((room) => {
|
...new Set(
|
||||||
if ("corporateCheque" in room.roomRate) {
|
rooms.map((room) => {
|
||||||
return CurrencyEnum.CC
|
if ("corporateCheque" in room.roomRate) {
|
||||||
} else if ("redemption" in room.roomRate) {
|
return CurrencyEnum.CC
|
||||||
return CurrencyEnum.POINTS
|
} else if ("redemption" in room.roomRate) {
|
||||||
} else if ("voucher" in room.roomRate) {
|
return CurrencyEnum.POINTS
|
||||||
return CurrencyEnum.Voucher
|
} else if ("voucher" in room.roomRate) {
|
||||||
} else if ("public" in room.roomRate && room.roomRate.public) {
|
return CurrencyEnum.Voucher
|
||||||
return room.roomRate.public.localPrice.currency
|
} else if (hasPublicPrice(room.roomRate)) {
|
||||||
} else if ("member" in room.roomRate && room.roomRate.member) {
|
return room.roomRate.public.localPrice.currency
|
||||||
return room.roomRate.member.localPrice.currency
|
} else if (hasMemberPrice(room.roomRate)) {
|
||||||
}
|
return room.roomRate.member.localPrice.currency
|
||||||
return CurrencyEnum.Unknown
|
}
|
||||||
})
|
return CurrencyEnum.Unknown
|
||||||
.join(","),
|
})
|
||||||
|
),
|
||||||
|
].join(","),
|
||||||
rewardNight: booking.searchType === REDEMPTION ? "yes" : "no",
|
rewardNight: booking.searchType === REDEMPTION ? "yes" : "no",
|
||||||
rewardNightAvailability:
|
rewardNightAvailability:
|
||||||
booking.searchType === REDEMPTION ? "true" : "false",
|
booking.searchType === REDEMPTION ? "true" : "false",
|
||||||
|
roomPrice: calcTotalRoomPrice(rooms, isMember),
|
||||||
|
totalPrice: calcTotalPrice(rooms, isMember),
|
||||||
searchTerm: city,
|
searchTerm: city,
|
||||||
searchType: "hotel",
|
searchType: "hotel",
|
||||||
specialRoomType: rooms
|
specialRoomType: rooms
|
||||||
@@ -134,8 +176,91 @@ export function getTracking(
|
|||||||
.join(","),
|
.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 {
|
return {
|
||||||
hotelsTrackingData,
|
hotelsTrackingData,
|
||||||
pageTrackingData,
|
pageTrackingData,
|
||||||
|
ancillaries,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasPublicPrice(
|
||||||
|
roomRate: Product
|
||||||
|
): roomRate is PriceProduct & { public: NonNullable<PriceProduct["public"]> } {
|
||||||
|
if ("public" in roomRate && roomRate.public) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasMemberPrice(
|
||||||
|
roomRate: Product
|
||||||
|
): roomRate is PriceProduct & { member: NonNullable<PriceProduct["member"]> } {
|
||||||
|
if ("member" in roomRate && roomRate.member) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasPetRoom(
|
||||||
|
room: Room
|
||||||
|
): room is Room & { packages: NonNullable<Room["packages"]> } {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -146,17 +146,19 @@ export function getTracking(
|
|||||||
.toLowerCase(),
|
.toLowerCase(),
|
||||||
//rateCodeType: , //TODO: Add when available in API. "regular, promotion, corporate etx",
|
//rateCodeType: , //TODO: Add when available in API. "regular, promotion, corporate etx",
|
||||||
region: hotel?.address.city,
|
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",
|
rewardNight: booking.roomPoints > 0 ? "yes" : "no",
|
||||||
rewardNightAvailability: booking.roomPoints > 0 ? "true" : "false",
|
rewardNightAvailability: booking.roomPoints > 0 ? "true" : "false",
|
||||||
points: booking.roomPoints > 0 ? booking.roomPoints : undefined,
|
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(","),
|
roomTypeCode: rooms.map((r) => r.roomTypeCode ?? "-").join(","),
|
||||||
searchType: "hotel",
|
searchType: "hotel",
|
||||||
specialRoomType: rooms
|
specialRoomType: rooms
|
||||||
.map((room) => getSpecialRoomType(room.packages))
|
.map((room) => getSpecialRoomType(room.packages))
|
||||||
.join(","),
|
.join(","),
|
||||||
totalPrice: rooms.map((r) => r.totalPrice).join(","),
|
totalPrice: rooms.reduce((total, room) => total + room.totalPrice, 0),
|
||||||
lateArrivalGuarantee: booking.rateDefinition.mustBeGuaranteed
|
lateArrivalGuarantee: booking.rateDefinition.mustBeGuaranteed
|
||||||
? "mandatory"
|
? "mandatory"
|
||||||
: isFlexBooking
|
: isFlexBooking
|
||||||
|
|||||||
@@ -70,11 +70,12 @@ export type TrackingSDKHotelInfo = {
|
|||||||
childBedPreference?: string
|
childBedPreference?: string
|
||||||
country?: string // Country of the hotel
|
country?: string // Country of the hotel
|
||||||
departureDate?: string
|
departureDate?: string
|
||||||
discount?: number
|
discount?: number | string
|
||||||
duration?: number // Number of nights to stay
|
duration?: number // Number of nights to stay
|
||||||
hotelID?: string
|
hotelID?: string
|
||||||
leadTime?: number // Number of days from booking date until arrivalDate
|
leadTime?: number // Number of days from booking date until arrivalDate
|
||||||
lowestRoomPrice?: number
|
lowestRoomPrice?: number
|
||||||
|
multiroomRateIdentity?: string
|
||||||
//modifyValues?: string // <price:<value>,roomtype:value>,bed:<value,<breakfast:value>
|
//modifyValues?: string // <price:<value>,roomtype:value>,bed:<value,<breakfast:value>
|
||||||
noOfAdults?: number | string // multiroom support, "2,1,3"
|
noOfAdults?: number | string // multiroom support, "2,1,3"
|
||||||
noOfChildren?: number | string // multiroom support, "2,1,3"
|
noOfChildren?: number | string // multiroom support, "2,1,3"
|
||||||
|
|||||||
Reference in New Issue
Block a user