Booking System Development on 1C-Bitrix
Booking is not just a form with dates. Under the hood: managing room inventory, checking availability for specific dates without overlaps, locking a slot during payment, automatic release on timeout, and integration with channel managers or online payment modules. Attempts to build this on top of the standard sale order module without proper architectural design lead to overbookings and duplicates.
Data model
A standard infoblock for accommodation objects is not suited for calendar-based availability tracking. A separate schema is required.
Object table — stored as an infoblock of type room (or a custom type hotel.room):
- Room type, capacity, base price, properties (
PROPERTY_ROOM_TYPE,PROPERTY_CAPACITY) - Link to the hotel infoblock via a binding property
Booking table — custom table bl_booking:
CREATE TABLE bl_booking (
id SERIAL PRIMARY KEY,
room_id INT NOT NULL, -- infoblock room element ID
user_id INT, -- b_user.ID
date_from DATE NOT NULL,
date_to DATE NOT NULL,
status VARCHAR(20) NOT NULL, -- pending, confirmed, cancelled, expired
order_id INT, -- b_sale_order.ID
price_total NUMERIC(12,2),
created_at TIMESTAMP DEFAULT NOW(),
expires_at TIMESTAMP, -- for pending bookings
guest_name VARCHAR(255),
guest_phone VARCHAR(50),
guest_email VARCHAR(255)
);
CREATE INDEX idx_booking_room_dates ON bl_booking(room_id, date_from, date_to, status);
Availability check
The key query — check for overlaps for the requested period:
SELECT COUNT(*) FROM bl_booking
WHERE room_id = :room_id
AND status IN ('pending', 'confirmed')
AND date_from < :date_to
AND date_to > :date_from;
If COUNT > 0 — the room is unavailable. The check is wrapped in a transaction with SELECT FOR UPDATE on the room record to prevent race conditions under concurrent requests.
$connection = \Bitrix\Main\Application::getConnection();
$connection->startTransaction();
// SELECT FOR UPDATE
// INSERT into bl_booking with status pending
// COMMIT
Locking and timeout
After a booking is created in pending status, a timer starts. If payment is not received within N minutes (typically 15–30), the booking transitions to expired and the slot is released.
Implementation via a 1C-Bitrix agent:
function ReleasExpiredBookings(): string
{
$expiredIds = BookingTable::getList([
'filter' => [
'STATUS' => 'pending',
'<=EXPIRES_AT' => new \Bitrix\Main\Type\DateTime(),
],
'select' => ['ID'],
])->fetchAll();
foreach ($expiredIds as $row) {
BookingTable::update($row['ID'], ['STATUS' => 'expired']);
}
return __FUNCTION__ . '();';
}
The agent is registered via CAgent::AddAgent() with a 60-second interval.
Date selection interface
The availability calendar is built on an AJAX request: the frontend requests /bitrix/services/main/ajax.php?action=BookingModule:getAvailability, and the backend returns an array of booked dates for the specific room. Flatpickr or Pikaday are used for visualization with unavailable days marked.
On the backend side — an AJAX controller, a descendant of \Bitrix\Main\Engine\Controller:
class BookingController extends \Bitrix\Main\Engine\Controller
{
public function getAvailabilityAction(int $roomId, string $month): array
{
// returns booked dates for the month
}
}
Case study: apart-hotel chain, 3 properties, 47 rooms
Task: replace manual booking by phone, eliminate overbooking.
Before: managers kept an Excel spreadsheet and reconciled once a week — double bookings and guest conflicts occurred regularly.
What was done:
-
roomsinfoblock with 47 elements, each with gallery and properties (FLOOR,VIEW,BED_TYPE) -
bl_bookingtable with an index on the date range - AJAX availability controller (response time 80–120 ms)
- Integration with payment acquiring via the
sale.paymentmodule: booking transitions toconfirmedvia a webhook from the payment gateway - Expired booking release agent running every 2 minutes
- Admin module with a calendar view of room occupancy
Result: zero overbookings over 14 months of operation, booking form conversion 4.2% (was 0% — all bookings went through phone calls).
| Stage | Duration |
|---|---|
| Data schema design | 3 days |
| Backend development (table, agent, controller) | 5 days |
| Frontend (calendar, form, AJAX) | 4 days |
| Payment gateway integration | 2 days |
| Admin interface | 3 days |
| Testing and launch | 2 days |
What is included in development
- Data model design accounting for room types and seasonal pricing
- Availability check mechanism with race condition protection
- Date selection interface with occupancy visualization
- Automatic expired booking release agent
- Integration with the
salemodule for invoicing and payment processing - Admin section for booking management







