Реализация бронирования ресурсов (залы, комнаты, оборудование) на сайте
Залы, переговорные комнаты, проектор, микрофонная стойка — это неодушевлённые ресурсы с фиксированным расписанием. Ключевое отличие от бронирования специалиста: один ресурс может бронироваться несколькими людьми одновременно (если это разрешено), а некоторые ресурсы можно бронировать только в комплекте.
Типы ресурсов и их особенности
| Тип | Особенности |
|---|---|
| Зал / комната | Один клиент за раз, минимальная длительность, кратность слота |
| Оборудование | Может быть несколько единиц одной позиции (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
);
Проверка доступности с учётом capacity
-- Сколько единиц ресурса занято в запрошенный интервал
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 рабочих дней. Несколько типов, capacity-aware проверка, буфер между бронями, календарный UI, управление исключениями в CMS — 8–12 рабочих дней.







