Готельний сайт відрізняється від звичайного каталогу тим, що відвідувач обирає не товар, а часовий слот. Номер сам по собі — набір характеристик (площа, місткість, вид з вікна). Але без вільних дат це мертва картка. Весь проєкт будується навколо календаря доступності та механіки бронювання, а не навколо красивої верстки. Проблема: відвідувач бачить красиві фото, але при спробі забронювати — календар не оновлений або ціна не відповідає сезону. Або інтеграція з 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С-Бітрікс. Замовте розробку сайту готелю — ми підберемо архітектуру під ваш номерний фонд. Отримайте консультацію щодо архітектури вашого сайту — оцінимо завдання та запропонуємо рішення.







