diff --git a/backend/services/forecasting/pickup_v2_model.py b/backend/services/forecasting/pickup_v2_model.py index 36ab3b3..f4ac2f8 100644 --- a/backend/services/forecasting/pickup_v2_model.py +++ b/backend/services/forecasting/pickup_v2_model.py @@ -91,15 +91,13 @@ async def get_current_otb_revenue(db, stay_date: date) -> Decimal: return Decimal('0') # Query actual bookings for real-time OTB revenue - # Exclude 'Departed' — guests who checked out early retain their original departure_date - # so would be double-counted; exclude 'Unconfirmed' for same reason on live OTB result = await db.execute( text(""" SELECT raw_json FROM newbook_bookings_data WHERE arrival_date <= :stay_date AND departure_date > :stay_date - AND status IN ('Confirmed', 'Arrived') + AND status IN ('Unconfirmed', 'Confirmed', 'Arrived', 'Departed') AND category_id = ANY(:categories) """), { @@ -306,14 +304,16 @@ async def get_current_otb_rooms_by_category(db, stay_date: date) -> Dict[str, in return {} # Query actual bookings for real-time OTB count - # Exclude 'Departed' — early checkouts keep their original departure_date so would inflate count + # COUNT DISTINCT room_number so that multiple bookings on the same room + # (e.g. early-checkout Departed + new Arrived) count as one occupied room. + # COALESCE to newbook_id ensures unassigned rooms (no room_number) still count. result = await db.execute( text(""" - SELECT category_id, COUNT(*) as room_count + SELECT category_id, COUNT(DISTINCT COALESCE(room_number, newbook_id)) as room_count FROM newbook_bookings_data WHERE arrival_date <= :stay_date AND departure_date > :stay_date - AND status IN ('Confirmed', 'Arrived') + AND status IN ('Unconfirmed', 'Confirmed', 'Arrived', 'Departed') AND category_id = ANY(:categories) GROUP BY category_id """),