Booking System Development for 1C-Bitrix
The standard 1C-Bitrix "Online Store" module is designed for products with immediate payment, not for services with time slots. Trying to book a client for a haircut, consultation, or class using only the standard cart and orders functionality leads to a pile of workarounds: product with zero stock = "occupied", XML field = "time", emails instead of a real schedule. A proper booking system requires a separate architecture.
Data Model: Schedule and Slots
The core of the system is a schedule iblock. Optimal structure:
Iblock "Services" (SERVICES_IBLOCK_ID):
- Standard fields: NAME, PREVIEW_TEXT, DETAIL_TEXT
- Properties:
DURATION(duration in minutes),PRICE,MAX_CAPACITY(group sessions),SPECIALIST_ID(link to specialist)
Iblock "Schedule" (SCHEDULE_IBLOCK_ID):
-
SERVICE_ID— link to service -
SPECIALIST_ID— performer -
DATE_FROM,DATE_TO— slot start and end (Date/Time property type) -
STATUS—free/booked/blocked -
BOOKING_ID— booking ID (set after reservation)
Bookings table (custom or via highload block):
CREATE TABLE bookings (
ID INT AUTO_INCREMENT PRIMARY KEY,
SLOT_ID INT NOT NULL, -- schedule element ID
USER_ID INT, -- user ID (NULL for unregistered)
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)
);
Schedule Slot Generation
Slots are generated based on working time templates. Approach: store weekly schedule templates per specialist and automatically create slots N weeks ahead.
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); // minutes
$current = clone $from;
while ($current <= $to) {
$dayOfWeek = (int)$current->format('N'); // 1=Mon, 7=Sun
$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');
}
}
}
Slots are created as iblock elements with status free. On booking — status changes to booked, on cancellation — back to free.
Time Selection Widget: Frontend Logic
The user selects a service → specialist → date → time. Each step is an AJAX request to a custom controller:
// /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'],
]);
// ...
}
}
On the frontend — a calendar (e.g. FullCalendar or a custom React one) highlighting available days and time slots.
Handling Race Conditions During Booking
If two users select the same slot simultaneously, you must guarantee only one receives the booking. Solution using optimistic locking at the DB level:
public function bookSlot(int $slotId, array $clientData): BookingResult
{
// Transaction + row lock
$connection = \Bitrix\Main\Application::getConnection();
$connection->startTransaction();
try {
// SELECT FOR UPDATE — lock the row
$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();
}
// Update slot status
$this->updateSlotStatus($slotId, 'booked');
// Create booking
$bookingId = $this->createBooking($slotId, $clientData);
$connection->commitTransaction();
return BookingResult::success($bookingId);
} catch (\Exception $e) {
$connection->rollbackTransaction();
throw $e;
}
}
Notifications and Reminders
After booking — an immediate notification to both the client and the specialist. 24 hours and 2 hours before — reminders. Implemented via Bitrix agents:
// Create a reminder agent on booking
\CAgent::AddAgent(
'\BookingModule\ReminderAgent::send(' . $bookingId . ');',
'my_booking_module',
'N', // non-periodic
0,
'',
'Y',
ConvertTimeStamp($bookingDateTs - 86400, 'FULL') // 24 hours before
);
Notification channels: email (via main module), SMS (via external gateway), Telegram bot. Notification templates — via standard Bitrix mail events (CEvent::Send).
Payment Integration
If the booking requires a prepayment — create an order in the Bitrix online store:
$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();
// Redirect to payment page
The booking-to-order relationship is stored in a table via the ORDER_ID field. On order payment — a webhook or OnSaleOrderPaid event handler confirms the booking.
Admin Interface
For administrators and specialists — a page in the /bitrix/admin/ section with:
- View of all bookings for a date (calendar view)
- Ability to block slots (vacation, lunch)
- Manual confirmation/cancellation with client notification
- CSV export for a date range
Development Stages
| Stage | Contents | Timeline |
|---|---|---|
| Design | Data model, usage scenarios | 3–5 days |
| Iblocks and slot generator | Creating structure, generating schedule | 1 week |
| AJAX controllers | Slot selection API, booking | 1 week |
| Frontend widget | Calendar, specialist and time selection | 1–2 weeks |
| Notifications | Email, SMS, reminders via agents | 3–5 days |
| Admin interface | Schedule management, booking view | 1 week |
| Payment integration (optional) | Prepayment via orders | 1 week |
Total: 6–10 weeks depending on the feature set.







