Реалізація бронювання ресурсів (зали, кімнати, обладнання) на веб-сайту
Зали, переговорні кімнати, проектор, мікрофонна стійка — це неживі ресурси з фіксованим розкладом. Ключова різниця від бронювання спеціаліста: один ресурс можуть бронювати кілька людей одночасно (якщо це дозволено), а деякі ресурси можна бронювати лише в комплекті.
Типи ресурсів та їхні особливості
| Тип | Особливості |
|---|---|
| Зал / кімната | Один клієнт за раз, мінімальна тривалість, кратність слота |
| Обладнання | Може бути кілька одиниць однієї позиції (3 проектори) |
| Парковочне місце | Фіксований слот, немає варіантів |
| Переговорна | Вміст обмежена, не можна бронювати на 2 години в середині робочого дня, якщо до/після залишається <30 хв |
Схема з урахуванням кількості одиниць
CREATE TABLE bookable_resources (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
resource_type VARCHAR(50),
capacity INTEGER DEFAULT 1, -- кількість одиниць (3 проектори → 3)
min_duration INTERVAL DEFAULT '1 hour',
max_duration INTERVAL,
slot_step INTERVAL DEFAULT '30 minutes', -- кратність
advance_booking INTERVAL DEFAULT '1 day', -- за скільки мінімум бронювати
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, -- скільки одиниць броніюється
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
);
Перевірка доступності з урахуванням вміститність
-- Скільки одиниць ресурсу займається в запитаному інтервалі
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, '[)');
Якщо booked_qty + requested_quantity <= capacity — слот доступний.
Правило «буфера між бронями»
Залам часто потрібен час на прибирання або підготовку між орендарями. Реалізується як налаштування ресурсу:
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
]
Календарний вид на фронтенді
Для залів найзручніший UI — Week view з колонками за ресурсами:
Час | Зал А | Зал Б | Переговорна
9:00 | [ВІЛЬНО] | [ЗАЙМАТО] | [ВІЛЬНО]
9:30 | [ЗАЙМАТО] | [ЗАЙМАТО] | [ВІЛЬНО]
10:00 | [ЗАЙМАТО] | [ВІЛЬНО] | [ЗАЙМАТО]
Для реалізації підходить FullCalendar (resourceTimeGrid view) з користувацьким backend-ом:
calendar = new FullCalendar.Calendar(el, {
plugins: ['resourceTimeGrid'],
initialView: 'resourceTimeGridDay',
resources: '/api/rooms',
events: '/api/bookings',
selectable: true,
select: (info) => openBookingModal(info),
});
Конфігурація в CMS
Адміністратор налаштовує через інтерфейс:
- Час роботи за днями тижня
- Святкові та вихідні дні (blackout dates)
- Мінімальний та максимальний термін бронювання
- Чи потрібне підтвердження чи броня автоматична
- Правила скасування (за скільки годин можна скасувати безкоштовно)
Строки реалізації
Бронювання одного типу ресурсу з базовим управлінням — 5–7 робочих днів. Декілька типів, перевірка з урахуванням вмістимості, буфер між бронями, календарний UI, управління винятками в CMS — 8–12 робочих днів.







