When forecast_covers_range or forecast_revenue_for_date throw DB exceptions,
the swallowed exception leaves the async session in an aborted transaction state.
Subsequent queries (DOW averages, per-date accom) then fail with
InFailedSQLTransactionError. Adding db.rollback() in the except blocks
resets the session so the fallback path can continue cleanly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The table is newbook_occupancy_report_data (per-category rows). Aggregate
SUM(available - maintenance) WHERE category_id != '5' to get the correct
available room count excluding overflow and maintenance.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace static system_config.total_rooms (80, includes overflow) with
per-day available_rooms from daily_occupancy table. This ensures
occupancy % is calculated against the correct base (non-overflow rooms)
consistent with how the rest of the app reports occupancy. Falls back
to system_config.total_rooms when daily_occupancy has no data for a date.
Also adds available_rooms per-day to the response payload.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Python FastAPI ML backend kept intact; auth replaced with central hnf_session cookie verification. Frontend rebuilt on React 18 + TS + Vite with stack design system, Plotly charts retained. Shared Postgres via DATABASE_URL; schema applied on startup.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>