Розробка функціоналу оренди товарів у 1С-Bitrix
Стандартний модуль sale у Bitrix заточений під продаж: товар → корзина → оплата → доставка. Оренда — інша модель: товар має часові слоти, ціна залежить від тривалості, один і той же SKU може бути «проданий» кільком клієнтам у різні дати, а після повернення знову доступний. Коробочний Bitrix це не вміває — потрібна розробка поверх існуючої архітектури каталога та замовлення.
Архітектура даних: що зберігати й де
Основна складність — модель доступності. Для продажу досить поля «залишок» в b_catalog_store_product. Для оренди потрібен календар бронювань: конкретні дати, коли одиниця товара занята.
Варіант 1 — highload-блок бронювань. Створюємо HL-блок RentalBooking з полями:
-
UF_PRODUCT_ID— прив'язка до SKU (елемент інфоблока торгових пропозицій) -
UF_UNIT_ID— ідентифікатор конкретної одиниці (якщо одного товара 5 штук, кожна одиниця відслідковується окремо) -
UF_DATE_FROM,UF_DATE_TO— період бронювання -
UF_ORDER_ID— зв'язок з замовленнямb_sale_order -
UF_STATUS— підтверджено / очікує оплату / повернено
Варіант 2 — окрема таблиця через модуль. Для проектів з високою навантаженням (прокат обладнання, десятки тисяч бронювань) HL-блок стає тормозом через EAV-зберігання. Створюємо свою таблицю:
CREATE TABLE b_rental_booking (
ID INT AUTO_INCREMENT PRIMARY KEY,
PRODUCT_ID INT NOT NULL,
UNIT_ID INT NOT NULL,
DATE_FROM DATE NOT NULL,
DATE_TO DATE NOT NULL,
ORDER_ID INT,
STATUS ENUM('pending','confirmed','returned','cancelled'),
INDEX idx_product_dates (PRODUCT_ID, DATE_FROM, DATE_TO)
);
Індекс по (PRODUCT_ID, DATE_FROM, DATE_TO) — обов'язковий, бо перевірка перетинів дат — основний запит системи.
Перевірка доступності та блокування
Головна інженерна задача — race condition. Два клієнти одночасно бронюють одну одиницю на одні дати. Стандартний CIBlockElement::GetList не захищає від цього.
Рішення — SELECT ... FOR UPDATE при створенні бронювання. Обернено в транзакцію:
-
BEGIN -
SELECT * FROM b_rental_booking WHERE PRODUCT_ID = ? AND UNIT_ID = ? AND STATUS IN ('pending','confirmed') AND DATE_FROM < ? AND DATE_TO > ? FOR UPDATE - Якщо рядків не знайдено —
INSERTнового бронювання -
COMMIT
У Bitrix це реалізується через $DB->StartTransaction() / $DB->Commit(). ORM D7 (Bitrix\Main\ORM) підтримує транзакції через Application::getConnection()->startTransaction().
Ціноутворення
Оренда передбачає ціну за одиницю часу: сутки, час, тиждень. Стандартний тип ціни в b_catalog_group зберігає фіксоване значення. Для оренди потрібна логіка перерахунку.
Властивості інфоблока для ціноутворення:
-
PRICE_PER_DAY— базова ставка за сутки -
MIN_RENTAL_DAYS— мінімальний термін -
DISCOUNT_WEEK— скидка при оренді на 7+ днів (відсоток) -
DISCOUNT_MONTH— скидка при оренді на 30+ днів
Розрахунок остаточної ціни виконується кастомним обробником події OnSaleBasketItemRefreshData. При пересчету корзини Bitrix викликає цей обробник, і ми підставляємо ціну виходячи з дат оренди, збережених у властивостях елемента корзини (BasketPropertyCollection).
Календар на фронтенді
Компонент вибору дат на сторінці товара. Мінімальна реалізація:
- AJAX-запит до кастомного контролера (
ajax.phpмодуля або REST-endpoint) - Контролер повертає масив занятих дат для конкретного товара
- На фронтенді — datepicker з заблокованими датами (flatpickr, react-datepicker або аналог)
- При виборі діапазону — повторний AJAX для розрахунку ціни та перевірки доступності
Занятті дати кешуються в b_cache_tag з тегом за ID товара. Інвалідація — при створенні, скасуванні або завершенні бронювання.
Життєвий цикл бронювання
| Етап | Подія Bitrix | Дія |
|---|---|---|
| Додавання в корзину | OnSaleBasketItemAdd |
Створення попереднього бронювання (status=pending), TTL 30 хвилин |
| Оплата замовлення | OnSalePayOrder |
Підтвердження бронювання (status=confirmed) |
| Скасування замовлення | OnSaleCancelOrder |
Звільнення дат (status=cancelled) |
| Повернення товара | Кастомний обробник | status=returned, одиниця знову доступна |
| Істечення TTL | Агент CAgent |
Видалення pending-бронювань старіших за 30 хвилин |
Агент для очистки зависших бронювань — критично важливий. Без нього корзини, кинуті на етапі оформлення, будуть блокувати товари навічно. Реєстрація агента через CAgent::AddAgent() з інтервалом 300 секунд.
Інтеграція з модулем sale
Властивості корзини (BasketPropertyCollection) зберігають дати оренди:
-
RENTAL_DATE_FROM -
RENTAL_DATE_TO -
RENTAL_UNIT_ID
Ці властивості додаються при виклику $basket->addItem() і використовуються при розрахунку ціни, формуванні друкованих документів і відображенні у особистому кабінеті.
Для відображення в адміністративному замовленні переопределяємо шаблон sale.admin.order.edit — додаємо колонки з датами оренди в таблицю складу замовлення.
Терміни реалізації
| Масштаб проекту | Обсяг робіт | Термін |
|---|---|---|
| Простий прокат (10–50 товарів, посічасова аренда) | HL-блок + обробники подій + datepicker | 1 тиждень |
| Середній (100+ товарів, почасова аренда, поштучний облік одиниць) | Своя таблиця + транзакції + агенти + інтеграція з ЛК | 1,5–2 тижні |
| Складний (мультисклад, залоги, штрафи за просрочку) | Повноцінний модуль з админкою, API та системою сповіщень | 2–3 тижні |
Ключове обмеження коробочного Bitrix — відсутність часового виміру в товарному залишку. Всі інше (корзина, оплата, сповіщення) працює штатно, якщо правильно реалізувати шар бронювань поверх стандартних механізмів.







