Гостиничный сайт отличается от обычного каталога тем, что посетитель выбирает не товар, а временной слот. Номер сам по себе — набор характеристик (площадь, вместимость, вид из окна). Но без свободных дат это мёртвая карточка. Весь проект строится вокруг календаря доступности и механики бронирования, а не вокруг красивой вёрстки. Проблема: посетитель видит красивые фото, но при попытке забронировать — календарь не обновлён или цена не соответствует сезону. Или интеграция с Booking.com отсутствует, и отель получает овербукинг. Мы решаем эти проблемы на уровне архитектуры: движок бронирования на Highload-блоках, сезонные тарифы, синхронизация с PMS.
Как работает система бронирования?
Ядро проекта — управление доступностью номеров. Задача: пользователь выбирает даты, система показывает доступные типы с ценами, бронирует и блокирует номер.
Хранение доступности — Highload-блок RoomInventory. Каждая строка — одна ночь для одного физического номера.
| Поле | Тип | Описание |
|---|---|---|
| UF_DATE | date | Дата ночи (например, 2025-07-15 — ночь с 15 на 16 июля) |
| UF_ROOM_ID | integer | ID физического номера |
| UF_ROOM_TYPE_ID | integer | ID типа номера |
| UF_STATUS | integer | 0 = свободен, 1 = забронирован, 2 = заблокирован, 3 = заселён |
| UF_BOOKING_ID | integer | ID заказа (из модуля sale) |
| UF_RATE | float | Тариф за эту ночь (с учётом сезона) |
Highload-блок — ORM-обёртка над таблицей с автоматическим API. При 100 номерах и горизонте 365 дней — 36 500 строк. Обязательные индексы: составной на (UF_DATE, UF_ROOM_TYPE_ID, UF_STATUS) и (UF_BOOKING_ID).
Алгоритм проверки доступности. Гость вводит check_in, check_out, guests. Система находит типы номеров с хотя бы одним физическим номером, свободным на все ночи.
public function getAvailableRoomTypes(
\Bitrix\Main\Type\Date $checkIn,
\Bitrix\Main\Type\Date $checkOut,
int $guests
): array {
$nights = $checkOut->getDiff($checkIn)->days;
$dates = [];
for ($i = 0; $i < $nights; $i++) {
$d = clone $checkIn;
$d->add(new \DateInterval("P{$i}D"));
$dates[] = $d->format('Y-m-d');
}
// Находим номера, занятые хотя бы в одну из ночей
$busyRooms = RoomInventoryTable::getList([
'select' => ['UF_ROOM_ID'],
'filter' => [
'UF_DATE' => $dates,
'!UF_STATUS' => 0,
],
'group' => ['UF_ROOM_ID'],
])->fetchAll();
$busyRoomIds = array_column($busyRooms, 'UF_ROOM_ID');
// Далее — исключаем занятые номера и фильтруем по вместимости
}
Подход через HAVING COUNT(*) = {$nights} корректнее:
SQL-запрос проверки доступности
SELECT UF_ROOM_ID, UF_ROOM_TYPE_ID
FROM hl_room_inventory
WHERE UF_DATE IN ('2025-07-15','2025-07-16','2025-07-17')
AND UF_STATUS = 0
GROUP BY UF_ROOM_ID, UF_ROOM_TYPE_ID
HAVING COUNT(*) = 3
Календарь доступности на фронте. Два поля — дата заезда и выезда. Реализация на flatpickr в режиме range. При открытии — AJAX-запрос за матрицей доступности: массив дат с признаком «есть свободные номера». Endpoint возвращает JSON:
{
"2025-07": {
"15": {"available": true, "min_rate": 4500},
"16": {"available": true, "min_rate": 4500},
"17": {"available": false, "min_rate": null},
"18": {"available": true, "min_rate": 6200}
}
}
Недоступные даты блокируются в календаре (disable). Минимальный тариф — при наведении. Запросы кэшируются через Bitrix\Main\Data\Cache с ключом availability_{month}_{year}.
Почему сезонное ценообразование увеличивает прибыль?
Highload-блок RatePlan:
| Поле | Тип |
|---|---|
| UF_ROOM_TYPE_ID | integer |
| UF_DATE_FROM | date |
| UF_DATE_TO | date |
| UF_WEEKDAY_RATE | float |
| UF_WEEKEND_RATE | float |
| UF_PRIORITY | integer |
При расчёте стоимости бронирования система перебирает каждую ночь, находит подходящий тарифный план и суммирует. Итоговая сумма — сумма по всем ночам.
Архитектура номерного фонда
Каждый тип номера — элемент инфоблока «Номерной фонд». Важно разделять тип (например, «Стандарт двухместный») и физические номера (20 комнат). Это ключевое архитектурное решение.
Структура инфоблока:
| Свойство | Тип | Назначение |
|---|---|---|
| CAPACITY | N (число) | Вместимость (основные места) |
| CAPACITY_EXTRA | N | Доп. места (раскладушка, детская кроватка) |
| AREA | N | Площадь, м² |
| AMENITIES | L (список, множественное) | Удобства: Wi-Fi, кондиционер, мини-бар, сейф |
| BED_TYPE | L (список) | Тип кровати: double, twin, king |
| VIEW | L (список) | Вид: море, город, сад, двор |
| FLOOR_RANGE | S (строка) | Этажи: «3-5» |
| GALLERY | F (файл, множественное) | Фотогалерея номера |
| PANORAMA_URL | S | Ссылка на 360-панораму |
| ROOM_COUNT | N | Количество физических номеров этого типа |
| MIN_STAY | N | Минимальное количество ночей |
| BASE_RATE | N | Базовый тариф за ночь (без сезонных наценок) |
Удобства (AMENITIES) — множественное свойство типа «Список». Не Highload-блок, потому что набор фиксирован (30-50 позиций). На фронте значения маппятся на иконки через конфиг.
Фотогалерея и виртуальный тур. Множественное свойство типа «Файл». Рендер — Swiper.js с lazy-загрузкой, превью через CFile::ResizeImageGet() на 600x400 с BX_RESIZE_IMAGE_PROPORTIONAL. Для 360-панорамы — Pannellum.js: библиотека принимает equirectangular-изображение и рендерит интерактивный обзор. Хранение — строковое свойство PANORAMA_URL. Pannellum инициализируется на клиенте:
pannellum.viewer('panorama-container', {
type: 'equirectangular',
panorama: roomData.panoramaUrl,
autoLoad: true,
compass: true,
hotSpots: [
{ pitch: -5, yaw: 120, type: 'info', text: 'Ванная комната' },
{ pitch: 0, yaw: 240, type: 'info', text: 'Балкон с видом на море' }
]
});
Hotspots задаются в JSON-свойстве инфоблока или в Highload-блоке, если нужна админ-панель.
Как iCal-синхронизация предотвращает овербукинг?
Гостиница продаёт номера на сайте и через OTA (Booking.com, Expedia). Без синхронизации — овербукинг. Channel Manager синхронизирует доступность и тарифы между PMS, сайтом и каналами.
iCal-синхронизация — простейший вариант. Booking.com и Airbnb отдают .ics-файлы. Битрикс-агент раз в 15 минут:
- Забирает
.icsпо URL (file_get_contentsили cURL) - Парсит
VEVENT— извлекаетDTSTART,DTEND,SUMMARY - Обновляет
RoomInventory:UF_STATUS = 2(заблокирован) - Генерирует исходящий
.icsс бронированиями сайта
Ограничение iCal: нет тарифов, задержка до 15 минут. Для 100+ номеров нужен API-коннектор. XML Push / API — через REST или SOAP (Booking.com Connectivity API). iCal-синхронизация в 10 раз проще API-интеграции, но менее гибкая.
PMS-интеграция. 1С:Отель — обмен через HTTP-сервис. Opera / Fidelio — SOAP с WSDL. Мы реализуем класс-обёртку над SoapClient с логированием.
Онлайн-оплата и предоплата. Бронирование через модуль sale. Заказ — одна позиция «Проживание в {тип}, {check_in} — {check_out}». Предоплата (20-30%) — кастомный обработчик OnSaleBeforeOrderAdd. Процент и первая ночь — типовые схемы.
Дополнительные модули
Личный кабинет гостя: авторизация (email + OAuth), мои бронирования, история поездок, программа лояльности. Баллы начисляются через обработчик OnSaleStatusOrderChange.
Мультиязычность: языковые версии (/en/, /de/), контент через свойства инфоблоков, hreflang.
SEO и микроразметка: Schema.org Hotel + HotelRoom + Offer. Разметка генерируется автоматически.
Фотоцентричный дизайн: WebP с fallback, <picture> с srcset, lazy-load. Оригиналы до 3000px, превью 800x600.
Отзывы: Highload-блок Reviews с модерацией. Агрегированный рейтинг в свойстве инфоблока AVG_RATING.
Что входит в работу
- Аналитика и прототипирование (карта номерного фонда, логика бронирования)
- Дизайн (фотоцентричный UI, мобильная версия, календарь)
- Ядро бронирования (RoomInventory, проверка доступности, оформление заказа, оплата)
- Интеграции (PMS, Channel Manager, платёжные системы, REST API)
- Контент и SEO (микроразметка, мультиязычность, мета-шаблоны)
- Тестирование и запуск (нагрузочное, кроссбраузерное, деплой)
- Документация и обучение администраторов
- Постпроектное сопровождение и гарантия
Этапы и сроки
| Масштаб | Сроки |
|---|---|
| Мини-отель, 10-20 номеров, базовое бронирование | 4–8 недель |
| Гостиница, 50-100 номеров, Channel Manager, PMS | 10–16 недель |
| Сеть отелей, мультисайт, программа лояльности | 16–24 недели |
Сроки не включают фотосъёмку и создание 360-панорам — это параллельный процесс.
Прямое бронирование через сайт в 2-3 раза выгоднее для отеля, чем через OTA, за счёт отсутствия комиссии. Экономия составляет 15-25% от выручки. Система бронирования окупается в среднем за 6-12 месяцев за счёт роста прямых продаж.
Мы работаем в гостиничной разработке более 8 лет, выполнили более 50 проектов для гостиничного бизнеса. Наши инженеры — сертифицированные специалисты 1С-Битрикс. Закажите разработку сайта гостиницы — мы подберём архитектуру под ваш номерной фонд. Получите консультацию по архитектуре вашего сайта — оценим задачу и предложим решение.







