Room Availability Calendar Setup in 1C-Bitrix
A hotel misses bookings not because there's no demand, but because guests don't see available dates in real time. Standard Bitrix catalog can't work with availability dates — this isn't its task. Booking calendar for rooms requires separate architecture: period occupancy storage, date intersection logic, and visual component.
Occupancy Period Storage
The central system element is the bookings table:
CREATE TABLE custom_room_bookings (
id INT AUTO_INCREMENT PRIMARY KEY,
room_id INT NOT NULL, -- Infoblock element ID (room)
order_id INT, -- Bitrix order link
guest_name VARCHAR(255),
check_in DATE NOT NULL,
check_out DATE NOT NULL,
status ENUM('pending','confirmed','cancelled') DEFAULT 'pending',
created_at DATETIME,
INDEX idx_room_dates (room_id, check_in, check_out),
INDEX idx_dates (check_in, check_out)
);
Room availability check for period is built on interval intersection query:
SELECT id FROM custom_room_bookings
WHERE room_id = :room_id
AND status != 'cancelled'
AND check_in < :check_out
AND check_out > :check_in
LIMIT 1;
If query returns row — room is occupied for requested period.
ORM Wrapper in D7
namespace Custom\Hotel;
class BookingTable extends \Bitrix\Main\ORM\Data\DataManager {
public static function getTableName(): string { return 'custom_room_bookings'; }
public static function isRoomAvailable(int $roomId, string $checkIn, string $checkOut): bool {
$result = static::getList([
'filter' => [
'=ROOM_ID' => $roomId,
'!=STATUS' => 'cancelled',
'<CHECK_IN' => $checkOut,
'>CHECK_OUT' => $checkIn,
],
'limit' => 1,
]);
return !$result->fetch();
}
public static function getOccupiedDates(int $roomId, string $month): array {
// Returns array of occupied dates for calendar
$from = date('Y-m-01', strtotime($month));
$to = date('Y-m-t', strtotime($month));
$bookings = static::getList([
'filter' => [
'=ROOM_ID' => $roomId,
'!=STATUS' => 'cancelled',
'<CHECK_IN' => $to,
'>CHECK_OUT' => $from,
],
]);
$dates = [];
while ($booking = $bookings->fetch()) {
$current = strtotime($booking['CHECK_IN']);
$end = strtotime($booking['CHECK_OUT']);
while ($current < $end) {
$dates[] = date('Y-m-d', $current);
$current = strtotime('+1 day', $current);
}
}
return array_unique($dates);
}
}
Calendar Visual Component
For display use either ready library (Flatpickr, Pikaday, Air Datepicker) or custom. Flatpickr is optimal: light (16 KB), supports date ranges, simple styling.
Flatpickr configuration with occupied dates:
async function initBookingCalendar(roomId) {
const response = await fetch(`/api/hotel/availability/?room_id=${roomId}&months=3`);
const { occupiedDates } = await response.json();
flatpickr('#date-range-picker', {
mode: 'range',
minDate: 'today',
dateFormat: 'Y-m-d',
locale: 'en',
disable: occupiedDates,
onChange: function(selectedDates) {
if (selectedDates.length === 2) {
const nights = Math.round(
(selectedDates[1] - selectedDates[0]) / 86400000
);
updatePricePreview(roomId, selectedDates[0], selectedDates[1], nights);
}
}
});
}
API Endpoint for Availability Data
AJAX controller returns occupied dates for requested period:
class HotelAvailabilityController extends \Bitrix\Main\Engine\Controller {
public function getAction(int $roomId, int $months = 2): array {
$occupiedDates = [];
$current = new \DateTime();
for ($m = 0; $m < $months; $m++) {
$monthStr = $current->format('Y-m');
$dates = BookingTable::getOccupiedDates($roomId, $monthStr);
$occupiedDates = array_merge($occupiedDates, $dates);
$current->modify('+1 month');
}
return ['occupiedDates' => array_unique($occupiedDates)];
}
}
Cache response — 5 minutes via \Bitrix\Main\Data\Cache, invalidate on new booking.
Date-Based Pricing
Hotels often apply seasonal rates. Room price stored not in trade offer (doesn't support periods) but in separate rates table:
CREATE TABLE custom_room_rates (
id INT AUTO_INCREMENT PRIMARY KEY,
room_id INT NOT NULL,
rate_from DATE NOT NULL,
rate_to DATE NOT NULL,
price_per_night DECIMAL(10,2) NOT NULL,
INDEX idx_room_period (room_id, rate_from, rate_to)
);
When dates selected, JavaScript requests total cost via separate endpoint, summing prices per night by active rates.
Integration with Bitrix Orders
Completed booking creates entry in custom_room_bookings and simultaneously creates order in sale module. This keeps single sales journal and allows using standard payment and notification mechanisms.
Execution Timeline
| Scope | Timeline |
|---|---|
| Storage + API + Flatpickr | 2–3 days |
| Seasonal rates + cost preview | +1–2 days |
| Order integration + notifications | +1–2 days |
| Channel Manager sync (OTA) | separate task |
Availability calendar is the foundation of online booking system. This is where customer makes purchase decision.

