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







