Skip to content

Fix revenue dashboard bugs (tenant leak, wrong totals, timezone, rounding) - #210

Open
karthikeyan18v wants to merge 4 commits into
Base360-AI:mainfrom
karthikeyan18v:main
Open

karthikeyan18v wants to merge 4 commits into
Base360-AI:mainfrom
karthikeyan18v:main

Conversation

@karthikeyan18v

Copy link
Copy Markdown

Logged in as both clients and went through the dashboard. Here's what I found and fixed:

  • Ocean seeing Sunset's numbers: the cache key was just revenue:{property_id}. Both tenants have a prop-001 so they were sharing the cached result. Added tenant_id to the key.

  • Numbers didn't match the DB at all: the db pool was using settings.supabase_db_* which don't exist, so it errored every time and fell back to the hardcoded mock data. Switched it to DATABASE_URL, fixed get_session, and removed the mock fallback. Also needed sqlalchemy[asyncio] for greenlet.

  • Sunset's March total was off: months were calculated in UTC. res-tz-1 is Feb 29 23:30 UTC but that's March 1 in Paris. Now it uses the property's timezone.

  • Few cents off: the total was sent as a float and rounded in the browser. Rounding on the backend with Decimal now.

  • Property dropdown was hardcoded with all 5 properties, so each client could see the other's. Now it loads from the API for the logged in tenant.

  • Unknown users got tenant-a: the tenant resolver defaulted anyone it didn't recognise to tenant-a, so they'd see Sunset's data. Now they get a 403. Asking for another tenant's property returns 404 instead of 0.00, and the currency comes from the reservations instead of being hardcoded.

  • Redis or DB going down broke the whole dashboard with a 500. Cache errors are skipped now so it reads from the db, and a db outage returns a 503 until it's back.

  • Profile page was broken in docker because .env is dockerignored, so the API url was empty. Added a fallback to localhost:8000.

Also added a month filter and a bookings list so you can check totals against the bookings.

To test: Sunset > Beach House Alpha > March 2024 should be 2,250.00 with 4 bookings. Ocean's prop-001 should show 0, and asking for the other tenant's property gives a 404.

Found these while going through the Sunset and Ocean accounts.

1. Ocean seeing Sunset's revenue
The revenue cache key was only the property id (revenue:prop-001). Both
tenants have a prop-001, so whoever loaded it first got cached and the
other tenant saw those numbers for the next 5 minutes. Added tenant_id to
the cache key.

2. Dashboard numbers not coming from the database
The db pool built its url from settings.supabase_db_*, which don't exist
in config. It also used QueuePool, which doesn't work with the async
engine, and get_session was async so "async with" failed on it. All of
that was caught by a try/except that returned a hardcoded mock dict, the
same for every tenant. Now it uses DATABASE_URL with one shared pool and
the mock fallback is removed. Added sqlalchemy[asyncio] so greenlet gets
installed, the async engine won't load without it.

3. Sunset's March total doesn't match
Month boundaries were done in UTC. res-tz-1 checks in at 2024-02-29 23:30
UTC, which is 1 March 00:30 in Paris, so for that property it's a March
booking. The month filter now converts check_in_date to the property's
timezone first.

4. Totals off by a few cents
Amounts are stored with 3 decimals and the total went out as a raw float
and got rounded in the browser. It's rounded to cents on the backend now,
using Decimal with half up rounding.

Also added two endpoints the dashboard uses: the tenant's properties, and
the reservations for a property (optionally for one month).
1. Every client saw every property
The property dropdown was a hardcoded list of all 5 properties from both
tenants, so Ocean saw Sunset's property names. prop-001 is also a
different property for each tenant. The list now comes from
/dashboard/properties for the logged in tenant.

2. Profile page failing to load
getApiBase() uses VITE_BACKEND_URL, but .env is in .dockerignore, so in
the docker build it was empty and the profile request went to nginx on
port 3000. nginx sent back index.html and parsing it as JSON failed. It
now falls back to localhost:8000, same as localAuthClient already does.

Also added a month picker and a bookings table under the revenue card,
so a month's total can be checked against the actual bookings.
- TenantResolver.resolve_tenant_id returned "tenant-a" for any user it
  didn't recognise, so they'd see Sunset's data. It reads tenant_id from
  the token first now, and returns None if it can't find one. The
  dashboard endpoints return 403 in that case instead of using a
  made-up "default_tenant".
- asking for a property that belongs to another tenant returned 0.00.
  It's a 404 now.
- currency was hardcoded to USD. It uses the reservations' currency now
  and returns 422 if a property has more than one.
- show an error on the dashboard if the property list fails to load.
- if redis was down, get/setex threw and the whole summary request
  failed with a 500, even though the db was fine. Cache errors are
  logged and skipped now, it just reads from the db.
- if the db was down the endpoints threw a 500 with a stack trace.
  They return a 503 "try again shortly" now, and recover on their own
  once the db is back.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant