What’s new
Updates and improvements to Whal. Newest first.
- FeatureFixSecurityHighlighted
Multi-tenant hardening, image uploads, and a fully furnished demo
A big day for the platform. Tenant isolation is now airtight, image uploads are live, the demo account got a complete makeover, and subdomains register themselves.
Automatic subdomain registration
New tenants no longer need a manual trip to the Vercel dashboard. When someone signs up, Whal calls the Vercel Domains API to register their subdomain automatically —
slug.whal.com.auis live by the time they finish the wizard. If the API is unreachable, signup continues and the domain can be added later.Image uploads via Vercel Blob
Three places in the app now support image uploads backed by Vercel Blob CDN storage:
- Products — upload a product photo from the product form (compact camera icon next to Category).
- Stock adjustments — attach evidence photos when recording damage, breakage, or corrections.
- Inbound receiving — document delivery condition with multiple photos per receipt.
Images are drag-and-drop or click-to-browse, with thumbnail previews and one-click removal. Files are validated for type and size (5 MB max) before upload.
Tenant isolation fixes
The warehouse config reader had an unsafe fallback: when the session's
tenantIdwas missing, it fell back toSELECT * FROM warehouse_config LIMIT 1with no tenant filter — silently reading another tenant's data. This was the root cause of warehouse types showing incorrectly across tenants.Both
getWarehouseConfig()andsaveWarehouseConfig()now refuse to operate without a valid tenant scope. Additionally, the JWT callback now backfillstenantIdfrom the database for sessions created before multi-tenancy was added, so stale tokens self-heal on the next request.Branded dashboard header
The dashboard header now shows the tenant's logo (compact pill mark), business name, and a Bonded/Standard badge — giving each warehouse its own identity instead of a generic page title.
Fully furnished demo warehouse
The demo account has been rewritten from scratch. Logging in as
demo@whal.com.aunow drops you into Sandbox Spirits Warehouse — a complete bonded warehouse with:- 3 clients (Sandbox Spirits, Coastal Distillery, Mountain Creek Wines)
- 16 products across gin, vodka, whisky, rum, liqueur, RTD, and wine
- A 4-zone warehouse layout with ~120 slots across 7 rows
- 30 pallets placed into specific locations, including one on hold and one quarantined
- 8 weeks of movement history, invoices, charges, dispatch requests, and alerts
The demo reset wipes all three clients and all tenant-scoped data (layout, config, fees, settings) before reseeding — so every visitor gets a clean warehouse.
Other fixes
- Trial banner hidden for demo users — the demo tenant doesn't have a real subscription, so "Trial · 90 days left" no longer appears.
- Trial banner hidden for super admins — platform operators don't need billing nags.
- Setup wizard pre-populates from existing config instead of overriding your signup choices with defaults.
- Signup form now defaults to "Bonded" warehouse type to match the DB default.
- FeatureHighlighted
Unit of Measure support — quantities in your preferred unit
Stock quantities are no longer locked to cartons. Whal now tracks every pallet in its product's base unit (typically bottles) and lets you view quantities in whichever unit makes sense for your workflow.
Display unit switcher
A new dropdown on the Stock, Outbound, Client Detail, and Portal Stock pages lets you flip between Bottle, Carton, Pallet, Drum, Keg, or Auto. Your choice is saved as a preference and applies everywhere until you change it. When a sub-label is useful (e.g. "50 carton (600 bottle)") you'll see both.
Per-product UOM setup
Products can now have a set of units with conversion factors — configured once, used everywhere. If a product doesn't have UOMs configured yet, Whal falls back to the legacy carton display so nothing breaks.
Unit picker on inbound, adjustments, and transfers
The stock initialisation walkthrough, adjustment form, and transfer form now include a unit picker next to the quantity field. The unit you choose is recorded on the movement for the audit trail.
Accurate excise calculations
Excise duty (LAL) is now computed from the base-unit quantity, removing an earlier assumption that all quantities were cartons. Bond-to-bond and duty-paid dispatches both benefit from the correction.
Warehouse map and portal
The slot detail panel on the warehouse map, the client portal stock view, and the cycle-count scope table all render through the new unit-aware display. The slot panel also now scrolls with the page on desktop so it stays visible while you browse tall rack layouts.
Backfill for existing data
A new script (
scripts/backfill-base-qty.mjs) can populate base-unit quantities on pallets and movements created before this update. Run it with--dry-runfirst to preview, then for real. The reconciliation script (scripts/reconcile-ledger.mjs) now checks both the legacy and base-unit ledger columns.