Разработка функционала аренды товаров на 1С-Битрикс
Стандартный модуль sale в Битрикс заточен под продажу: товар → корзина → оплата → доставка. Аренда — другая модель: товар имеет временные слоты, цена зависит от длительности, один и тот же SKU может быть «продан» нескольким клиентам в разные даты, а после возврата снова доступен. Коробочный Битрикс этого не умеет — нужна разработка поверх существующей архитектуры каталога и заказа.
Архитектура данных: что хранить и где
Основная сложность — модель доступности. Для продажи достаточно поля «остаток» в 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
В Битрикс это реализуется через $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. При пересчёте корзины Битрикс вызывает этот обработчик, и мы подставляем цену исходя из дат аренды, хранящихся в свойствах элемента корзины (BasketPropertyCollection).
Календарь на фронтенде
Компонент выбора дат на странице товара. Минимальная реализация:
- AJAX-запрос к кастомному контроллеру (
ajax.phpмодуля или REST-endpoint) - Контроллер возвращает массив занятых дат для конкретного товара
- На фронте — datepicker с заблокированными датами (flatpickr, react-datepicker или аналог)
- При выборе диапазона — повторный AJAX для расчёта цены и проверки доступности
Занятые даты кешируются в b_cache_tag с тегом по ID товара. Инвалидация — при создании, отмене или завершении бронирования.
Жизненный цикл бронирования
| Этап | Событие Битрикс | Действие |
|---|---|---|
| Добавление в корзину | 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 недели |
Ключевое ограничение коробочного Битрикс — отсутствие временного измерения в товарном остатке. Всё остальное (корзина, оплата, уведомления) работает штатно, если правильно реализовать слой бронирований поверх стандартных механизмов.







