from .hospitality_occupancy_adr_revpar_types import HotelPerformance from .math_round_div import round_div from .money_amount import Money, money def _whole(value: object) -> bool: return isinstance(value, int) and not isinstance(value, bool) def occupancy_adr_revpar(room_nights_available: int, room_nights_sold: int, room_revenue: Money) -> HotelPerformance: """Occupancy, ADR and RevPAR for a period. Each figure is divided out of the raw totals and rounded half-up once. RevPAR is revenue over available rooms, not ADR times occupancy: multiplying two figures that were each rounded already can be a penny out. """ if not _whole(room_nights_available) or room_nights_available < 1: raise ValueError("roomNightsAvailable must be at least 1, received %s" % (room_nights_available,)) if not _whole(room_nights_sold) or room_nights_sold < 0: raise ValueError("roomNightsSold must not be negative, received %s" % (room_nights_sold,)) if room_nights_sold > room_nights_available: raise ValueError( "roomNightsSold (%d) cannot exceed roomNightsAvailable (%d)" % (room_nights_sold, room_nights_available) ) if room_revenue.minor < 0: raise ValueError("roomRevenue must not be negative, received %d" % (room_revenue.minor,)) return HotelPerformance( room_nights_available=room_nights_available, room_nights_sold=room_nights_sold, room_revenue=room_revenue, occupancy_basis_points=round_div(room_nights_sold * 10000, room_nights_available, "half-up"), adr=None if room_nights_sold == 0 else money(round_div(room_revenue.minor, room_nights_sold, "half-up"), room_revenue.currency), revpar=money(round_div(room_revenue.minor, room_nights_available, "half-up"), room_revenue.currency), )