Upload monthly entity reports
Drop the month's report files (the daily-grid .xlsx exports). Each file is parsed, validated against the check battery, and previewed below. Nothing is written to the database until you press Commit.
Journal Voucher
Anomalies
The full check battery, run on demand against everything committed. Filter by month or severity. This is your "tap anomalies" view — it re-scans live data, so it stays current as you load more months.
Report Tracker
Which expected reports are received vs pending for a month — measured against the benchmark roster, not just whatever happened to arrive. Pick any month, including one you haven't loaded yet, to see what's still outstanding.
Benchmark roster
The expected list. Set each property's expected-from month so it isn't counted pending before it started; set expected-to if it stops reporting; untick active to pause it. Add new properties here.
Aliases (internal / Cloudbeds names → property)
When a report's internal title differs from your roster name — a rename like Claremont → St Augustine, or a typo — register it here. The upload check uses this to recognise the property instead of blocking. An unregistered name mismatch is exactly what gets flagged as a possible mislabel.
Statutory rates — drive the Journal Voucher
The rates the JE actually posts (Sales / Lodging / Resort per city). Editing these recomputes every JV for that city. Airbnb is charged Resort only.
Expected collected rates — drive the tax-deviation flag
What you expect each city (or a specific property) to have actually collected, as an effective % of non-Airbnb lodging. The Anomalies tab flags any property-month whose collected rate drifts outside expected ± tolerance. A per-entity row overrides its city. This is separate from statutory because your data shows collected ≠ statutory (e.g. Miami collects ~10.5% vs a 13% table).
Room Analysis
Room-nights and per-room metrics by property across months — your comparison base for volume-driven costs like cleaning and laundry. Pick a metric; the grid is property × month. Cleaning / room-night is the one to line up against a cleaning or laundry invoice — a stable property whose per-room cost jumps is worth a question.
Vendor Bills — cost vs room-nights
Enter a vendor bill (cleaning, laundry, etc.) against a property + month, and it's costed per room-night using that month's occupancy. The vs trend column compares each bill's cost-per-room-night to that property+service's own average across other months — a bill that's well outside its own history is the one to question. (Bill reading/OCR is a planned add-on; for now enter or CSV-import the figures.)
CSV columns: property, month (YYYY-MM), vendor, service_type, amount
Channel economics
Per-channel revenue, the OTA commission, effective commission %, and net payout to owner — for a selected month. Commission is captured from the report going forward; months without it show blank until re-imported.
Commission expectations — switchable, drive the flags
For each channel group, whether it should book commission, and its expected rate. When on, a channel producing revenue with $0 commission flags revenue-no-commission; a rate outside expected ± tolerance flags commission-out-of-band. Expedia defaults ON (its reports show $0) — switch it off if Expedia is netted/merchant-model so it stops flagging.
Sales tax reconciliation — collected / owed vs paid
Log what the client paid to each tax authority (upload the return PDF to read it, or enter manually) and it's reconciled against what our data says was owed at the statutory rate and collected from guests for the same jurisdiction + period. A negative variance = under-remitted.
History — re-upload audit trail
Every load and re-sync, with date/time and who did it. A re-sync shows exactly what changed field-by-field (and what didn't) against the previously-loaded data. Click a row to expand the change detail.
Access control — who can see this data
Only emails on this list can see any data, even after signing in. This is the security gate. Two steps to onboard a teammate: (1) approve their email here, and (2) create their login in Supabase → Authentication → Add user with that same email (Auto Confirm ticked). Removing an email here instantly cuts their access.
Users & Roles
Set each person's role and (for Team members) which properties they can see or edit. Tiers: super_admin (owner) · admin (runs ops, no user mgmt) · admin_view (read-only admin) · team_member (only granted properties).
Create a login
Creates the account directly — no dashboard step. You set a temporary password and share it with them; they can change it after signing in. (Email invites need a verified sender domain, so this uses a shared temp password instead.)
Property grants
Team members see/edit only the properties checked here. View = read; Edit = can upload/change that property's data.
Reporting lines
Who reports to whom. A person can have more than one manager. The owner edits these inline; everyone else sees them read-only.
Access Map
Who can do what, at a glance — capabilities down, people across. Derived from each person's role.
Activity Log
Append-only record of who did what — sign-ins, commits, exports, role and data changes. Owner-only.
DB Audit & Health
Runs on open. The coverage probe flags any rev_ table with RLS off or zero policies (grows automatically as tables are added). The health checks exercise each subsystem end-to-end for your current login.
RLS coverage
Health checks
Settings
Portal-wide configuration. Owner-only. Session limits apply to everyone on next check.
Security & two-factor authentication
Two-factor (2FA) adds a 6-digit code from an authenticator app (Google Authenticator, Authy, 1Password) on top of your password. This protects your account. The owner can require it for everyone under Settings.
Set up an authenticator
- Scan this QR code with your authenticator app (or type the key manually).
- Enter the 6-digit code it shows to confirm.