iOS Safari has no beforeinstallprompt API, so the portal's Install
button (?install=1) silently did nothing there. IosInstallHint now
shows Share -> Add to Home Screen steps on Safari, or a prompt to
switch to Safari first if opened in another iOS browser/in-app
webview (those can't install PWAs on iOS at all).
Also added apple-mobile-web-app-title + apple-touch-icon so the
home screen icon isn't a page screenshot.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
"Fill F&B from last 7 days" only drew from the current month's elapsed
days, so early in a month (e.g. the 3rd) it only had 1-2 actual days to
repeat instead of a true trailing 7-day window. Add a dedicated
last7-fnb endpoint that fetches a real 7-day window ending yesterday,
reaching back into the prior month when needed.
Arrivals, departures, stayovers, and bookings-by-source were sending
request params (arrival_date_from, departure_date_from, stayover_date_from)
and reading response fields (arrival_date, booking_ref, no_nights) that
don't exist in NewBook's API — causing API errors. Switched to the real
bookings_list contract (period_from/period_to/list_type) and field names
(booking_arrival, booking_departure, reference, booking_length, etc.)
already proven working in room-planner, hvac, and kitchen's integrations.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Table now matches the format used elsewhere in the app: one row per
meter (Consumption/Usage/Standing/Extras/VAT/Total/Basis/Period
Estimate) instead of a category rollup, mirroring the utilities app's
own Reports/Estimates pages (basis text, merged extras column).
Adds two charts below the table: total utility cost (this year vs
prior year) and one small per-category consumption chart (this year
vs prior year), both fed by the new /api/internal/trend endpoint,
aligned to the report's own month so paging to an older week shows
the right trailing-12-month window rather than today's.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The utilities app's backend isn't published to the host, only its
nginx frontend on :3080. Its internal.js routes are mounted at bare
/api/internal/... with no slug prefix, so callers must go through the
/utilities/api/ nginx location (which strips the prefix and forwards
to the backend's /api/... path) rather than hitting /api/internal/...
directly — the latter has no matching nginx location and 404s.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Follows the same app_settings/DB pattern already used for the
Forecasting integration: utilities_url and utilities_api_key are now
editable from Reports Settings, with env vars kept only as a fallback.
Avoids needing to SSH in and hand-edit .env to authenticate against
the utilities app's internal API.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replaces the Utilities placeholder with a cost-breakdown table by
category (usage/standing/CCL/RAB levy/fixed extras/VAT/total) sourced
from the utilities app's internal API, plus a projected-total note for
the current month. Mirrors the existing forecasting integration
pattern (best-effort fetch, doesn't break the rest of the report on
failure). Also adds the missing UTILITIES_URL/UTILITIES_API_KEY env
vars to docker-compose.yml, needed by both this and the pre-existing
directors-forecast integration.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The report endpoint's totalPickup/totalRooms summed every day's
pickup override regardless of whether the day had already passed,
even though a past day's actual accommodation revenue already
supersedes it (fAccomm ignores pickup for those days). This
overstated Probable Pickup and the month's rooms estimate. Also
fixed pickup.accomm to sum each day's own rate instead of
multiplying the total by a flat session rate.
Adds a utilities-client.js mirroring the existing forecasting-client
HTTP pattern (UTILITIES_URL/UTILITIES_API_KEY). Directors report
response gains a separate `utilities` field; degrades gracefully with
an error string if the utilities app is unreachable or unconfigured.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Same fix as maintenance/room-planner: manifest scope was the app's
own base path instead of "/", and AuthGate unconditionally hard-
navigated window.top to the central /login on session expiry even
when not embedded in the portal iframe — together these dropped an
installed/standalone reports PWA into the portal's framed browser
view instead of staying in its own window.
AuthGate now only bounces to central login when actually embedded
(passing ?from= so it returns here afterwards); standalone or
directly-opened tabs get an in-app login form and never navigate
away. Also wired up the previously-dead inactivity auto-logout timer.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Worksheet save now persists forecast_dry/forecast_wet per day to day_overrides
so the report page always shows the same totals the user saw on the worksheet,
rather than making a second independent forecasting API call that can return
slightly different values.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Checkbox in the header switches between 364-day same-weekday comparison
and same calendar-date last year. Default remains DOW-aligned.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Past dates already excluded from pickup calculation; suppress display to
avoid giving the impression the stored override is still active.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Past dates: zero out pickup (override ignored), use actuals for dry/wet
(fall back to override/forecast only if no actual exists), lock dry/wet
inputs read-only. Pickup + OTB total is now capped at available rooms
so occupancy can't exceed 100%. Totals row also excludes past pickup.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removed display:block override on .wa-charts-row; charts that sit
two-per-row on screen now do the same on paper instead of stacking.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- Always fetch full calendar month (not truncated at week ending)
- Add month_daily[] to response (per-day TY/LY by dept, is_actual flag)
Frontend:
- New 'Sales History' section with:
- Monthly dept line chart: 12-month x-axis, TY vs prior year per dept
(Rooms/Dry/Wet solid, LY dashed — same colour per dept)
- Rolling 12-month average trend (moved from Month Progress)
- Month Progress section gains cumulative line chart:
- X-axis = day of month, 6 lines (3 TY solid + 3 LY dashed)
- TY lines stop at week ending; LY spans full month
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Override overflow:hidden on .page-content and .app-shell so the full
report flows to paper instead of clipping at screen height
- break-inside:avoid on each section so they land whole on a page
- Charts stack vertically on print (full width each) rather than 2-col grid
- Hide sidebar, toolbar; reset backgrounds and shadows for clean output
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Stacked bar chart (Rooms/Dry/Wet) for the previous 12 months and last
12 months side by side, with a reference line marking the boundary.
No extra API calls — data comes from the existing 36-month fetch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
toISOString() returns UTC, so midnight local in BST (UTC+1) lands on
the previous UTC day, making each arrow step 6 days instead of 7.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace raw monthly totals with a proper rolling 12-month average trend.
Fetches 36 months of data; the 12 display points each show the average
of the 12 months ending on that point (TY) and the same window shifted
back a year (LY), producing a smooth upward trend vs last year.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Full weekly actuals report matching the spreadsheet layout:
- Week summary table (Rooms/Dry/Wet vs Budget vs Last Year vs Last Week)
- Grouped bar chart + dept split pie chart
- 5-week rolling summary table
- Occupancy table by day of week (rooms, occ%, ARR, RevPAR)
- Month progress to date (totals + dept split) with progress bar chart
- 12-month turnover trend line chart by dept
- Utilities placeholder (pending meter readings app)
Budget values come from the forecasting API which distributes monthly
budget proportionally to daily sales — so high-demand weeks (e.g.
Christmas) carry a higher budget share. Gross shown as net × 1.20.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Switch from a single total_rooms scalar to rm.available_rooms per day,
now that the forecasting public API returns it from daily_occupancy.
Fixes worksheet Avail column showing 80 (total physical beds) instead
of the correct occupancy-reportable room count.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add explicit generic return types to dfGetWorksheet/dfSaveWorksheet/
dfGetReport so TypeScript can infer the response shape in the component.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace queryForecast/forecast-db.js with fetch() calls to the
forecasting app's public API (/forecasting/api/public/forecast/revenue
and /forecast/rooms). This removes the need for cross-DB grants and the
FORECAST_DATABASE_URL — all data comes from FORECASTING_API_KEY +
FORECASTING_URL. The DOW-history algorithm is also removed since the
covers model in the forecasting API already handles dry/wet future
forecasts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Embeds a Directors Forecast section in the Reports sidebar (hard-coded
under "Directors Reports"). Worksheet tab: day-by-day OTB grid with
editable pickup rooms, dry/wet overrides, ML suggestion column and live
forecast recalculation. Report tab: forecast vs budget vs last-year
comparison, occupancy summary, weekly revenue bands and snapshot tracking.
Backend: forecast-db.js (read-only pool to forecasting_db), new tables
(forecast_sessions, day_overrides, forecast_snapshots) and full REST
routes under /api/directors-forecast. docker-compose FORECAST_DATABASE_URL
added.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>