The JS Mini Hotel system provides three powerful reporting functions to help you analyze hotel performance and guest behavior. This guide covers occupancy reports, revenue calculations, and guest history lookups.
Report Types Overview
Occupancy Report View room occupancy for any specific date, including percentage and breakdown by room type.
Revenue Report Calculate total revenue for a date range, including room charges and extras.
Guest History Look up all reservations and spending for a specific guest by email.
Occupancy Reports
Generate a snapshot of hotel occupancy for any date (past, present, or future).
Using getOccupancyReport()
const report = getOccupancyReport ( hotel , '2026-03-22' )
console . log ( report )
How It Works
The function analyzes all reservations (active and historical) to determine which rooms are occupied on a specific date:
function getOccupancyReport ( hotel , date ) {
const reservationsPlusHistory = structuredClone ([
... hotel . reservations ,
... hotel . history
])
// Find rooms occupied on this date
const occupiedRooms = reservationsPlusHistory
. map ( reservation => {
if ( isDateInRange ( date , reservation . checkIn , reservation . checkOut )) {
return getRoomByNumber ( hotel , reservation . roomNumber )
}
})
. filter ( room => room !== undefined )
return {
date: date . toString (),
total: hotel . rooms . length ,
occupied: occupiedRooms . length ,
available: hotel . rooms . length - occupiedRooms . length ,
percentage: Math . floor (( occupiedRooms . length / hotel . rooms . length ) * 100 ),
byType: {
single: {
total: getRoomsByType ( hotel . rooms , ROOM_TYPE . SINGLE ). length ,
occupied: getRoomsByType ( occupiedRooms , ROOM_TYPE . SINGLE ). length
},
double: {
total: getRoomsByType ( hotel . rooms , ROOM_TYPE . DOUBLE ). length ,
occupied: getRoomsByType ( occupiedRooms , ROOM_TYPE . DOUBLE ). length
},
suite: {
total: getRoomsByType ( hotel . rooms , ROOM_TYPE . SUITE ). length ,
occupied: getRoomsByType ( occupiedRooms , ROOM_TYPE . SUITE ). length
}
}
}
}
A room is considered occupied on a date if: checkIn <= date && date < checkOut
Note that the check-out date is exclusive - a guest checking out on March 22 does not occupy the room on March 22.
Expected Output Structure
const report = getOccupancyReport ( hotel , '2026-03-22' )
Use Cases
Daily Operations
Future Planning
Historical Analysis
Check current day occupancy: const today = Temporal . Now . plainDateISO (). toString ()
const todayReport = getOccupancyReport ( hotel , today )
console . log ( `Today's occupancy: ${ todayReport . percentage } %` )
console . log ( `Available rooms: ${ todayReport . available } ` )
Forecast occupancy for upcoming dates: const futureDate = '2026-06-15'
const forecast = getOccupancyReport ( hotel , futureDate )
if ( forecast . percentage < 50 ) {
console . log ( 'Low occupancy - consider promotional offers' )
}
Review past occupancy: const pastDate = '2026-01-15'
const historical = getOccupancyReport ( hotel , pastDate )
console . log ( `January 15 occupancy was ${ historical . percentage } %` )
The report includes both active reservations and historical data, so it can generate accurate reports for any date.
Revenue Reports
Calculate total revenue for a date range, including breakdowns for room charges and extras.
Using getRevenueReport()
const revenue = getRevenueReport ( hotel , '2026-01-01' , '2026-03-22' )
console . log ( revenue )
How It Works
The function finds all reservations that overlap with the date range and calculates revenue:
function getRevenueReport ( hotel , startDate , endDate ) {
const reservationsPlusHistory = getReservationsPlusHistory ( hotel )
const reservationsInRange = reservationsPlusHistory . reduce (
( acc , reservation ) => {
if (
isRangeInRange (
startDate ,
endDate ,
reservation . checkIn ,
reservation . checkOut
) &&
reservation . status !== RESERVATION_STATUS . CANCELLED
) {
acc . push ( reservation )
}
return acc
},
[]
)
return {
period: ` ${ startDate . toString () } to ${ endDate . toString () } ` ,
rooms: hotel . rooms . length ,
extras: reservationsInRange . reduce (( acc , reservation ) => {
return ( acc += getExtrasPrice ( reservation . extras ))
}, 0 ),
total: reservationsInRange . reduce (( acc , reservation ) => {
return ( acc += reservation . totalPrice )
}, 0 ),
reservations: reservationsInRange . length
}
}
Cancelled reservations are excluded from revenue calculations, even if they had cancellation penalties.
Expected Output Structure
const revenue = getRevenueReport ( hotel , '2026-01-01' , '2026-01-31' )
Revenue Calculation Examples
// Get revenue for January 2026
const januaryRevenue = getRevenueReport (
hotel ,
'2026-01-01' ,
'2026-01-31'
)
console . log ( `January revenue: € ${ januaryRevenue . total } ` )
console . log ( `From extras: € ${ januaryRevenue . extras } ` )
console . log ( `Number of reservations: ${ januaryRevenue . reservations } ` )
// Q1 2026 revenue
const q1Revenue = getRevenueReport (
hotel ,
'2026-01-01' ,
'2026-03-31'
)
// Calculate average per reservation
const avgPerReservation = q1Revenue . total / q1Revenue . reservations
console . log ( `Average per reservation: € ${ avgPerReservation . toFixed ( 2 ) } ` )
const today = Temporal . Now . plainDateISO (). toString ()
const ytdRevenue = getRevenueReport ( hotel , '2026-01-01' , today )
console . log ( `Year-to-date revenue: € ${ ytdRevenue . total } ` )
Understanding the Total Field
The total field includes both room charges and extras: total = ( sum of all reservation . totalPrice values )
// Where each reservation.totalPrice already includes:
// - Room charges (nights × pricePerNight)
// - Extras charges (sum of extra.price × extra.quantity)
Guest History
Look up all reservations and total spending for a specific guest.
Using getGuestHistory()
How It Works
The function searches both active reservations and history by guest email:
function getGuestHistory ( hotel , guestEmail ) {
const reservations = getReservationsPlusHistory ( hotel )
const reservationsOfGuest = reservations . filter (
reservation => reservation . guest . email === guestEmail
)
const guest = reservationsOfGuest . reduce (( acc , reservation ) => {
return ( acc = reservation . guest )
}, {})
const totalSpent = reservationsOfGuest . reduce (( acc , reservation ) => {
return ( acc += reservation . totalPrice )
}, 0 )
const totalExtras = reservationsOfGuest . reduce (( acc , reservation ) => {
return ( acc += getExtrasPrice ( reservation . extras ))
}, 0 )
return {
guestEmail ,
guestName: guest . name ,
totalReservations: reservationsOfGuest . length ,
totalSpent: totalSpent + totalExtras ,
reservations: reservationsOfGuest . map ( reservation => {
return {
id: reservation . id ,
checkIn: reservation . checkIn ,
checkOut: reservation . checkOut ,
status: reservation . status ,
totalPrice: reservation . totalPrice
}
})
}
}
The function calculates totalSpent by adding totalSpent + totalExtras. This appears to double-count extras since totalPrice already includes extras. This might be a bug in the source code.
Expected Output Structure
Guest History Use Cases
Loyalty Programs
Customer Service
Marketing Analytics
Identify frequent guests: const history = getGuestHistory ( hotel , '[email protected] ' )
if ( history . totalReservations >= 5 ) {
console . log ( ` ${ history . guestName } is a VIP guest!` )
console . log ( `Total spent: € ${ history . totalSpent } ` )
}
Review guest’s reservation history: const history = getGuestHistory ( hotel , '[email protected] ' )
console . log ( `Guest: ${ history . guestName } ` )
console . log ( `Total stays: ${ history . totalReservations } ` )
// Show upcoming reservations
const upcoming = history . reservations . filter (
r => r . status === 'confirmed' || r . status === 'checked-in'
)
console . log ( `Upcoming reservations: ${ upcoming . length } ` )
Analyze guest spending: const history = getGuestHistory ( hotel , '[email protected] ' )
const avgSpend = history . totalSpent / history . totalReservations
console . log ( `Average spend per visit: € ${ avgSpend . toFixed ( 2 ) } ` )
Combining Reports
You can combine different reports for comprehensive insights:
// Get all data for a month
const month = '2026-01'
const startDate = ` ${ month } -01`
const endDate = ` ${ month } -31`
// Revenue report
const revenue = getRevenueReport ( hotel , startDate , endDate )
// Occupancy samples (first, middle, last day)
const day1 = getOccupancyReport ( hotel , ` ${ month } -01` )
const day15 = getOccupancyReport ( hotel , ` ${ month } -15` )
const day31 = getOccupancyReport ( hotel , ` ${ month } -31` )
// Calculate average occupancy
const avgOccupancy = Math . round (
( day1 . percentage + day15 . percentage + day31 . percentage ) / 3
)
console . log ( 'Monthly Report' )
console . log ( '==============' )
console . log ( `Revenue: € ${ revenue . total } ` )
console . log ( `Reservations: ${ revenue . reservations } ` )
console . log ( `Average occupancy: ${ avgOccupancy } %` )
console . log ( `Revenue per reservation: € ${ ( revenue . total / revenue . reservations ). toFixed ( 2 ) } ` )
Best Practices
Use appropriate date formats
Check for empty guest history: const history = getGuestHistory ( hotel , '[email protected] ' )
if ( history . totalReservations === 0 ) {
console . log ( 'Guest not found' )
} else {
console . log ( `Found ${ history . totalReservations } reservations` )
}
For frequently accessed reports, consider caching: const reportCache = new Map ()
function getCachedOccupancyReport ( hotel , date ) {
const key = `occupancy- ${ date } `
if ( ! reportCache . has ( key )) {
reportCache . set ( key , getOccupancyReport ( hotel , date ))
}
return reportCache . get ( key )
}
All report functions iterate through reservations and history. For hotels with thousands of reservations, consider:
Implementing pagination for guest history
Adding date range filters to limit data processing
Caching frequently accessed reports
Next Steps
Creating Reservations Create new reservations that will appear in reports
Managing Extras Add extras that contribute to revenue reports