Development of a booking system on 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1173
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    745
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

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:

  • rooms infoblock with 47 elements, each with gallery and properties (FLOOR, VIEW, BED_TYPE)
  • bl_booking table with an index on the date range
  • AJAX availability controller (response time 80–120 ms)
  • Integration with payment acquiring via the sale.payment module: booking transitions to confirmed via 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 sale module for invoicing and payment processing
  • Admin section for booking management