Inventory & suppliers
Room Inventory by Date/Occupancy (Hotels)
Indicative price, not an offer: €64/mo; calendar view, occupancy blocking, overbooking prevention
Generated from docs/plan/paid/room-inventory-by-date-occupancy/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
A paid inventory module for hospitality merchants (hotels, resorts, short-term rentals) managing rooms as date-based inventory. Replaces flat SKU inventory with calendar-aware blocking, occupancy queries by date range, and overbooking prevention. Essential for any room-rental business where inventory is not a single qty but a time-bound availability matrix.
The problem
I have 50 rooms; each can be booked on different dates. Standard inventory counts stock as one number; I need to know if Room 201 is available June 1–5, and prevent double-booking. Existing product inventory cannot express ‘Room 201 is blocked June 2 for maintenance’ or ‘booked June 1–3’.
What it does
- Room entity linked to product (room as product variant or separate inventory type)
- Date-range occupancy: booked_from, booked_to, reason (guest_order_id, maintenance, block_out)
- Calendar view: 12-month grid showing all rooms, color-coded by status (available, booked, blocked)
- Availability query: GET /api/rooms/availability/:checkInDate/:checkOutDate returns available room list
- Block creation: staff can manually block dates for maintenance, overbooking hold, or special event
- Occupancy validation on order checkout: reject if any night in requested range is booked
- Partial-night support: 2-night minimum, same-day checkout rules, timezone-aware date boundaries
- Overbooking detection: warn if blocking X nights would exceed room count for any night
- Occupancy report: GET /api/rooms/:roomId/occupancy-history returns all bookings/blocks for audit
- Admin calendar editor: drag-to-block, click-to-unblock, bulk block by reason (e.g., ‘all rooms June 1 for inspection’)
What it deliberately does NOT do
Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.
- Full PMS (property management system) — no guest check-in/check-out process, no housekeeping workflow, no maintenance work orders
- Guest management or profiles — this is inventory only; guest data lives in orders/customers
- Revenue management or dynamic pricing — no rate optimization; pricing stays in product config
- Inter-property transfers or multi-location reconciliation — each shop is independent; no corporate rollup
Data model
New tables: room_inventory (id, product_id, room_number, room_name, capacity, created_at), room_occupancy (id, room_id, booked_from_date, booked_to_date, reason, related_order_id, created_by, created_at), room_blocks (id, room_id, blocked_from_date, blocked_to_date, block_reason, block_type enum(guest_order, maintenance, event, hold), created_at). Inventory table extended with room_id and optional date_range_from/date_range_to if storing non-room inventory.
API
- GET /api/rooms
- POST /api/rooms
- GET /api/rooms/:id/occupancy-calendar/:year/:month
- GET /api/rooms/availability/:checkInDate/:checkOutDate
- POST /api/rooms/:id/occupancy
- GET /api/rooms/:id/occupancy-history
- POST /api/rooms/:id/block
- DELETE /api/rooms/:id/occupancy/:occupancyId
- POST /api/rooms/bulk-block
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
Calendar view with 12-month grid, drag-to-block UI, color legend (booked=blue, blocked=red, available=green), room detail sidebar showing upcoming bookings, bulk-block dialog for maintenance windows, occupancy history with edit/delete controls.
The seam — why this is paid
Core owns the date-aware inventory interface and occupancy query logic. Paid owns calendar conflict resolution, overbooking prevention algorithms, occupancy audit trails, and multi-night validation rules.
Support commitment: calendar conflict resolution, double-booking prevention, occupancy audits
Dependencies
- product module (room as product)
- order module (links occupancy to order_id)
- date/calendar handling (timezone support)
Acceptance checks
Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.
- POST occupancy with overlapping dates on same room is rejected
- GET availability/:checkIn/:checkOut returns only rooms with zero occupancy across all nights
- Calendar shows booked and blocked dates in correct color
- Bulk-block successfully blocks one date across multiple rooms
- Occupancy history includes deleted occupancy with deletion timestamp and user
- Same-day checkout does not block next day (date boundary timezone-aware)
Risks
Date boundary errors (UTC vs local time breaks multi-day logic); concurrent booking of same room race condition; bulk-block rolls back on error for one room (partial commit); occupancy query timeout on 10,000+ room/date combinations; guest cancellation does not auto-free room.
Commercial context
| Suggested price | €64/mo; calendar view, occupancy blocking, overbooking prevention |
| Rival anchor | Opera/Fosse PMS: €500-1000/mo; custom: €5000+ |
The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.
Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full
catalogue and ../../AI-GUIDE.md for how to work on this repository.