Разработка системы записи на услуги на 1С-Битрикс
Стандартный модуль «Интернет-магазин» Битрикса заточен под товары с немедленной оплатой, а не под услуги с временными слотами. Попытка записать клиента на стрижку, консультацию или занятие, используя только стандартный функционал корзины и заказов — приводит к нагромождению костылей: товар с нулевым остатком = «занято», XML-поле = «время», email-ы вместо реального расписания. Полноценная система записи требует отдельной архитектуры.
Модель данных: расписание и слоты
Ядро системы — инфоблок расписания. Оптимальная структура:
Инфоблок «Услуги» (SERVICES_IBLOCK_ID):
- Стандартные поля: NAME, PREVIEW_TEXT, DETAIL_TEXT
- Свойства:
DURATION(продолжительность в минутах),PRICE,MAX_CAPACITY(групповые занятия),SPECIALIST_ID(привязка к специалисту)
Инфоблок «Расписание» (SCHEDULE_IBLOCK_ID):
-
SERVICE_ID— привязка к услуге -
SPECIALIST_ID— исполнитель -
DATE_FROM,DATE_TO— начало и конец слота (свойства типа «Дата/время») -
STATUS—free/booked/blocked -
BOOKING_ID— ID записи (после бронирования)
Таблица бронирований (кастомная или через highload-блок):
CREATE TABLE bookings (
ID INT AUTO_INCREMENT PRIMARY KEY,
SLOT_ID INT NOT NULL, -- ID элемента расписания
USER_ID INT, -- ID пользователя (NULL для незарегистрированных)
CLIENT_NAME VARCHAR(255),
CLIENT_PHONE VARCHAR(20),
CLIENT_EMAIL VARCHAR(255),
STATUS ENUM('pending','confirmed','cancelled','completed') DEFAULT 'pending',
COMMENT TEXT,
CREATED_AT DATETIME,
UPDATED_AT DATETIME,
INDEX (SLOT_ID),
INDEX (STATUS)
);
Генерация слотов расписания
Слоты генерируются на основе шаблонов рабочего времени. Подход: хранить шаблоны недельного расписания специалиста и автоматически создавать слоты на N недель вперёд.
class SlotGenerator
{
public function generateForSpecialist(int $specialistId, \DateTime $from, \DateTime $to): void
{
$schedule = $this->getWeeklySchedule($specialistId); // [0=>[], 1=>['09:00','13:00',...]]
$serviceDuration = $this->getServiceDuration($specialistId); // минуты
$current = clone $from;
while ($current <= $to) {
$dayOfWeek = (int)$current->format('N'); // 1=пн, 7=вс
$daySlots = $schedule[$dayOfWeek] ?? [];
foreach ($daySlots as $timeRange) {
[$start, $end] = explode('-', $timeRange); // '09:00-13:00'
$this->createSlotsBetween($specialistId, $current, $start, $end, $serviceDuration);
}
$current->modify('+1 day');
}
}
}
Слоты создаются как элементы инфоблока со статусом free. При бронировании — статус меняется на booked, при отмене — обратно на free.
Виджет выбора времени: логика на фронте
Пользователь выбирает услугу → специалиста → дату → время. Каждый шаг — AJAX-запрос к кастомному контроллеру:
// /local/ajax/booking.php
class BookingController extends \CBitrixComponent
{
public function getAvailableSlots(int $specialistId, string $date): array
{
$dateFrom = new \Bitrix\Main\Type\DateTime($date . ' 00:00:00');
$dateTo = new \Bitrix\Main\Type\DateTime($date . ' 23:59:59');
$result = \Bitrix\Iblock\ElementTable::getList([
'filter' => [
'IBLOCK_ID' => SCHEDULE_IBLOCK_ID,
'=PROPERTY_SPECIALIST_ID' => $specialistId,
'>=PROPERTY_DATE_FROM' => $dateFrom,
'<=PROPERTY_DATE_FROM' => $dateTo,
'=PROPERTY_STATUS' => 'free',
'=ACTIVE' => 'Y',
],
'select' => ['ID', 'PROPERTY_DATE_FROM', 'PROPERTY_DATE_TO'],
'order' => ['PROPERTY_DATE_FROM' => 'ASC'],
]);
// ...
}
}
На фронте — календарь (например, FullCalendar или кастомный на React) с подсветкой доступных дней и временных слотов.
Обработка гонки условий при бронировании
Если два пользователя одновременно выбрали один слот, нужно гарантировать, что только один получит бронь. Решение через оптимистичную блокировку на уровне БД:
public function bookSlot(int $slotId, array $clientData): BookingResult
{
// Транзакция + блокировка строки
$connection = \Bitrix\Main\Application::getConnection();
$connection->startTransaction();
try {
// SELECT FOR UPDATE — блокируем строку
$slot = $connection->query(
"SELECT * FROM b_iblock_element_property
WHERE IBLOCK_ELEMENT_ID = {$slotId} AND IBLOCK_PROPERTY_ID = " . STATUS_PROP_ID . "
FOR UPDATE"
)->fetch();
if ($slot['VALUE'] !== 'free') {
$connection->rollbackTransaction();
return BookingResult::slotTaken();
}
// Обновляем статус слота
$this->updateSlotStatus($slotId, 'booked');
// Создаём бронирование
$bookingId = $this->createBooking($slotId, $clientData);
$connection->commitTransaction();
return BookingResult::success($bookingId);
} catch (\Exception $e) {
$connection->rollbackTransaction();
throw $e;
}
}
Уведомления и напоминания
После бронирования — немедленное уведомление клиенту и специалисту. За 24 часа и за 2 часа — напоминания. Реализация через агенты Битрикса:
// Создаём агент-напоминание при бронировании
\CAgent::AddAgent(
'\BookingModule\ReminderAgent::send(' . $bookingId . ');',
'my_booking_module',
'N', // не периодический
0,
'',
'Y',
ConvertTimeStamp($bookingDateTs - 86400, 'FULL') // за 24 часа
);
Канал уведомлений: email (через модуль main), SMS (через внешний шлюз), Telegram-бот. Шаблоны уведомлений — через стандартные почтовые события Битрикса (CEvent::Send).
Интеграция с оплатой
Если запись требует предоплаты — создаём заказ в интернет-магазине Битрикса:
$order = \Bitrix\Sale\Order::create(SITE_ID, $userId);
$order->setPersonTypeId(1);
$basket = $order->getBasket();
$item = \Bitrix\Sale\BasketItem::create($basket, 'catalog', $serviceElementId);
$item->setFields(['QUANTITY' => 1, 'PRICE' => $servicePrice, 'NAME' => $serviceName]);
$basket->addItem($item);
$order->save();
// Перенаправляем на страницу оплаты
Связь бронирования с заказом хранится в таблице через поле ORDER_ID. При оплате заказа — вебхук или обработчик события OnSaleOrderPaid подтверждает бронирование.
Административный интерфейс
Для администраторов и специалистов — страница в разделе /bitrix/admin/ с:
- Просмотром всех броней на дату (calendar view)
- Возможностью блокировать слоты (отпуск, обед)
- Ручным подтверждением/отменой с отправкой уведомления клиенту
- Выгрузкой в CSV за период
Этапы разработки
| Этап | Содержание | Срок |
|---|---|---|
| Проектирование | Модель данных, сценарии использования | 3–5 дней |
| Инфоблоки и генератор слотов | Создание структуры, генерация расписания | 1 неделя |
| AJAX-контроллеры | API выбора слота, бронирования | 1 неделя |
| Фронтенд-виджет | Календарь, выбор специалиста и времени | 1–2 недели |
| Уведомления | Email, SMS, напоминания через агенты | 3–5 дней |
| Административный интерфейс | Управление расписанием, просмотр броней | 1 неделя |
| Интеграция с оплатой (опционально) | Предоплата через заказы | 1 неделя |
Суммарно: 6–10 недель в зависимости от набора функций.







