Налаштування модуля онлайн-бронювання номерів на 1С-Бітрікс
У 1С-Бітрікс немає стандартного модуля бронювання номерів. Є модуль sale (замовлення), інфоблоки, агенти. З цього збирається система бронювання, але розрив між «є інструменти» і «бронювання працює» — це кілька тижнів проєктування і розробки. Головна технічна проблема, яку вирішує модуль: атомарна перевірка доступності із захистом від одночасного резервування одного номера двома користувачами.
Структура даних
Типова схема для об'єктів розміщення:
Інфоблок hotel_rooms — каталог номерів:
-
PROPERTY_ROOM_TYPE— тип (стандарт, люкс, апартаменти) -
PROPERTY_CAPACITY— місткість -
PROPERTY_AREA— площа -
PROPERTY_FLOOR— поверх -
PROPERTY_BED_TYPE— тип ліжок (одне двоспальне, два односпальних) -
PROPERTY_AMENITIES— список зручностей (множинна властивість)
Таблиця бронювань bl_room_booking:
CREATE TABLE bl_room_booking (
id SERIAL PRIMARY KEY,
room_id INT NOT NULL,
order_id INT REFERENCES b_sale_order(ID),
user_id INT REFERENCES b_user(ID),
date_from DATE NOT NULL,
date_to DATE NOT NULL,
nights SMALLINT GENERATED ALWAYS AS (date_to - date_from) STORED,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
rate_code VARCHAR(64),
adults SMALLINT DEFAULT 1,
children SMALLINT DEFAULT 0,
price_night NUMERIC(10,2),
price_total NUMERIC(10,2),
guest_name VARCHAR(255),
guest_email VARCHAR(255),
guest_phone VARCHAR(50),
comment TEXT,
created_at TIMESTAMP DEFAULT NOW(),
expires_at TIMESTAMP,
CONSTRAINT chk_dates CHECK (date_to > date_from)
);
CREATE INDEX idx_booking_room_dates ON bl_room_booking(room_id, date_from, date_to) WHERE status IN ('pending', 'confirmed');
Атомарна перевірка і створення бронювання
Race condition — головний ворог систем бронювання. Два користувачі одночасно відкрили форму на один номер, обидва перевірили доступність (вільно), обидва натиснули «Забронювати». Без блокування обидва отримують підтвердження.
Рішення — транзакція з явним блокуванням рядка:
$connection = \Bitrix\Main\Application::getConnection();
$connection->startTransaction();
try {
// Блокуємо запис номера для оновлення
$connection->query(
"SELECT id FROM bl_room_booking
WHERE room_id = ?
AND status IN ('pending', 'confirmed')
AND date_from < ?
AND date_to > ?
FOR UPDATE",
[$roomId, $dateTo, $dateFrom]
);
$conflicts = $connection->getAffectedRowsCount();
if ($conflicts > 0) {
$connection->rollbackTransaction();
return ['error' => 'Номер недоступний на обрані дати'];
}
$bookingId = $connection->query(
"INSERT INTO bl_room_booking
(room_id, user_id, date_from, date_to, status, expires_at, price_total)
VALUES (?, ?, ?, ?, 'pending', NOW() + INTERVAL '20 minutes', ?)
RETURNING id",
[$roomId, $userId, $dateFrom, $dateTo, $totalPrice]
)->fetch()['id'];
$connection->commitTransaction();
return ['booking_id' => $bookingId];
} catch (\Exception $e) {
$connection->rollbackTransaction();
throw $e;
}
Зв'язок із модулем sale
Після створення бронювання зі статусом pending створюємо замовлення в модулі sale:
$order = \Bitrix\Sale\Order::create(SITE_ID, $userId);
$order->setField('CURRENCY', 'RUB');
$basket = $order->getBasket();
$item = \Bitrix\Sale\BasketItem::create($basket, 'catalog', $roomProductId);
$item->setFields([
'NAME' => 'Номер ' . $roomName . ' (' . $nights . ' ночей)',
'QUANTITY' => 1,
'PRICE' => $totalPrice,
'CURRENCY' => 'RUB',
]);
$basket->addItem($item);
$order->save();
// Прив'язуємо order_id до бронювання
BookingTable::update($bookingId, ['ORDER_ID' => $order->getId()]);
При оплаті замовлення (подія OnSalePaymentEntitySaved, IS_PAID = Y) переводимо бронювання в confirmed.
Агент звільнення прострочених бронювань
Бронювання зі статусом pending та вичерпаним expires_at мають звільнятися автоматично:
function ReleaseExpiredRoomBookings(): string
{
\Bitrix\Main\Application::getConnection()->queryExecute(
"UPDATE bl_room_booking
SET status = 'expired'
WHERE status = 'pending' AND expires_at < NOW()"
);
// Скасовуємо пов'язані замовлення в sale
$expired = \Bitrix\Main\Application::getConnection()->query(
"SELECT order_id FROM bl_room_booking WHERE status = 'expired' AND order_id IS NOT NULL AND notified = false"
);
while ($row = $expired->fetch()) {
$order = \Bitrix\Sale\Order::load($row['order_id']);
if ($order) $order->setField('STATUS_ID', 'CANCEL');
}
return __FUNCTION__ . '();';
}
Агент реєструється з інтервалом 60 секунд.
Адміністративний інтерфейс
До /bitrix/admin/ додається розділ «Бронювання». Ключові представлення:
-
Календарна сітка — рядки = типи номерів, стовпці = дати. Комірки пофарбовані за статусом бронювання. Реалізується через кастомну сторінку з таблицею з
bl_room_booking. - Список бронювань — стандартний грід із фільтрами за статусом, датами, гостем.
- Картка бронювання — деталі, кнопки зміни статусу, прив'язане замовлення.
Терміни
| Етап | Термін |
|---|---|
| Інфоблок номерів + схема БД | 2 дні |
| Бекенд: перевірка, створення, агент | 4 дні |
| Форма бронювання на сайті (AJAX, календар) | 3 дні |
| Зв'язок із модулем sale і платіжними системами | 2 дні |
| Адміністративний інтерфейс | 3 дні |
| Тестування | 2 дні |
| Разом | 2–3 тижні |







