Custom 1С-Bitrix Booking System: Eliminating Overbooking
Overbooking is a headache for any hotel business. When two guests simultaneously book the last room, a standard Bitrix cannot guarantee that no double booking occurs. Losses from such conflicts can reach 500,000 rubles per year for a small network. We developed a custom booking system on 1С-Bitrix that completely eliminates overbooking through transactional row locking and automatic release of expired bookings. Custom development of a booking system on 1С-Bitrix starts with database schema design: the bl_booking table, indexes, and transactions. Under the hood: SELECT FOR UPDATE, an agent, and an AJAX calendar. Below is the architecture and a real case.
According to 1С-Bitrix documentation, for complex booking logic it is recommended to use custom tables instead of information blocks. We use exactly this approach: an rooms information block for room descriptions, and a custom bl_booking table for occupancy tracking. This provides flexibility in queries and performance.
Why Standard Information Blocks Don't Work for Booking?
Calendar-based availability tracking requires a separate schema. Information blocks cannot efficiently check date overlaps and lock slots.
Room table — information block of type room with properties PROPERTY_ROOM_TYPE, PROPERTY_CAPACITY and binding to hotel.
Booking table — custom table bl_booking:
CREATE TABLE bl_booking ( id SERIAL PRIMARY KEY, room_id INT NOT NULL, user_id INT, date_from DATE NOT NULL, date_to DATE NOT NULL, status VARCHAR(20) NOT NULL, -- pending, confirmed, cancelled, expired order_id INT, price_total NUMERIC(12,2), created_at TIMESTAMP DEFAULT NOW(), expires_at TIMESTAMP, 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); How Does the Availability Check with Race Condition Protection Work?
The key query checks for overlaps:
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 query is wrapped in a transaction with SELECT FOR UPDATE to eliminate race conditions. Implementation steps:
- Open transaction.
- Execute SELECT FOR UPDATE on the room record.
- Check date overlaps.
- If free — INSERT into bl_booking with status pending.
- Commit transaction.
Under parallel requests, the second request waits for the first transaction to finish, guaranteeing no double bookings. The custom check based on bl_booking runs 5 times faster than attempting similar logic on standard info blocks.
How Is Payment Timeout Handled?
After creating a booking in pending status, a timer starts. If payment is not received within 15–30 minutes, the booking transitions to expired and the slot is released.
Implementation via an 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__ . '();'; } Registered via CAgent::AddAgent() with a 60-second interval.
How Is the Date Picker Interface Built?
The availability calendar is built using an AJAX request to /bitrix/services/main/ajax.php?action=BookingModule:getAvailability. The backend returns occupied dates. On the frontend we use Flatpickr with disabled day marking.
AJAX controller extending \Bitrix\Main\Engine\Controller:
class BookingController extends \Bitrix\Main\Engine\Controller { public function getAvailabilityAction(int $roomId, string $month): array { // returns occupied dates for the month } } Case Study: Apartment Hotel Network (3 properties, 47 rooms)
Task: Replace manual phone-based booking, eliminate overbooking.
Initial situation: Managers used an Excel spreadsheet, reconciled weekly — periodic double bookings led to guest complaints. Overbooking losses before implementation were about 500,000 rubles per year.
Solutions implemented:
- Information block
roomswith 47 elements, each with gallery and properties (FLOOR,VIEW,BED_TYPE) -
bl_bookingtable with date range index - AJAX controller for availability check (responds in 80–120 ms)
- Integration with payment gateway via
sale.paymentmodule: booking transitions to confirmed on webhook from payment gateway - Agent to release expired bookings every 2 minutes
- Administrative module with calendar view of room occupancy
Results: Zero overbookings over 14 months of operation, booking form conversion rate 4.2% (was 0% — everything went through phone). Development costs recovered in 2 months.
Development Process
| Stage | Duration |
|---|---|
| Data schema design | 3 days |
| Backend development (table, agent, controller) | 5 days |
| Frontend (calendar, form, AJAX) | 4 days |
| Integration with payment gateway | 2 days |
| Admin interface | 3 days |
| Testing and launch | 2 days |
Timelines may vary depending on integration complexity and number of properties.
Approach Comparison
| Aspect | Standard sale module | Custom bl_booking |
|---|---|---|
| Date overlap check | Requires complex modifications | Built-in, fast |
| Booking timeout | None, only manual cancellation | Automatic agent |
| Race condition | Not resolved | SELECT FOR UPDATE |
| Check performance | ~500 ms | 80–120 ms |
What Does the Booking System Development Include?
- Data model design with room types and seasonal pricing
- Availability check mechanism with race condition protection
- Date selection interface with occupancy visualization
- Automatic agent for releasing expired bookings
- Integration with
salemodule for invoicing and payment acceptance - Administrative section for booking management
- Guest and admin notification setup (email/SMS)
Get a consultation for your project. Order a turnkey booking system development — we will prepare a commercial proposal within 1 business day. Contact us to estimate development timelines and cost for your tasks.

