Implementing Resource Booking (Halls, Rooms, Equipment) on a Website
Halls, meeting rooms, projectors, microphone stands — these are inanimate resources with fixed schedules. Key difference from specialist booking: one resource can be booked by multiple people simultaneously (if allowed), and some resources can only be booked as a package.
Resource Types and Their Features
| Type | Features |
|---|---|
| Hall / room | One client at a time, minimum duration, slot rounding |
| Equipment | May have multiple units of one item (3 projectors) |
| Parking spot | Fixed slot, no options |
| Meeting room | Limited capacity, can't book for 2 hours in the middle of business day if <30 min remains before/after |
Schema Accounting for Units
CREATE TABLE bookable_resources (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
resource_type VARCHAR(50),
capacity INTEGER DEFAULT 1, -- number of units (3 projectors → 3)
min_duration INTERVAL DEFAULT '1 hour',
max_duration INTERVAL,
slot_step INTERVAL DEFAULT '30 minutes', -- rounding
advance_booking INTERVAL DEFAULT '1 day', -- minimum booking advance
max_lookahead INTERVAL DEFAULT '90 days',
location VARCHAR(255),
amenities TEXT[],
images JSONB DEFAULT '[]',
is_active BOOLEAN DEFAULT TRUE
);
CREATE TABLE bookings (
id BIGSERIAL PRIMARY KEY,
resource_id INTEGER REFERENCES bookable_resources(id),
quantity SMALLINT DEFAULT 1, -- how many units are booked
starts_at TIMESTAMP NOT NULL,
ends_at TIMESTAMP NOT NULL,
status VARCHAR(20) DEFAULT 'pending',
booker_name VARCHAR(255),
booker_email VARCHAR(255),
purpose TEXT,
attendees_count INTEGER,
metadata JSONB
);
Checking Availability with Capacity
-- How many resource units are occupied in the requested interval
SELECT COALESCE(SUM(quantity), 0) AS booked_qty
FROM bookings
WHERE resource_id = $1
AND status NOT IN ('cancelled')
AND tsrange(starts_at, ends_at, '[)') && tsrange($2::timestamp, $3::timestamp, '[)');
If booked_qty + requested_quantity <= capacity — the slot is available.
Buffer Time Between Bookings Rule
Halls often need time for cleaning or preparation between tenants. Implemented as a resource setting:
CLEANUP_BUFFER = timedelta(minutes=30)
def get_effective_booked_intervals(resource_id: int, date: date) -> list[Interval]:
raw = get_bookings(resource_id, date, status_not_in=['cancelled'])
return [
Interval(
start=b.starts_at - CLEANUP_BUFFER,
end=b.ends_at + CLEANUP_BUFFER,
)
for b in raw
]
Calendar View on Frontend
For halls, the most convenient UI is Week view with columns for resources:
Time | Hall A | Hall B | Meeting Room
9:00 | [FREE] | [BOOKED] | [FREE]
9:30 | [BOOKED] | [BOOKED] | [FREE]
10:00 | [BOOKED] | [FREE] | [BOOKED]
FullCalendar (resourceTimeGrid view) with custom backend is suitable:
calendar = new FullCalendar.Calendar(el, {
plugins: ['resourceTimeGrid'],
initialView: 'resourceTimeGridDay',
resources: '/api/rooms',
events: '/api/bookings',
selectable: true,
select: (info) => openBookingModal(info),
});
Configuration in CMS
Administrator configures through interface:
- Working hours by day of week
- Holiday and non-working days (blackout dates)
- Minimum and maximum booking duration
- Whether confirmation is required or booking is automatic
- Cancellation rules (how many hours in advance to cancel for free)
Implementation Timeline
Booking one resource type with basic management — 5–7 business days. Multiple types, capacity-aware checking, buffer between bookings, calendar UI, managing exceptions in CMS — 8–12 business days.







