Configuring Rates and Seasonal Pricing for Accommodation in 1C-Bitrix
Hotel pricing is more complex than in an online store. A single room can have different prices depending on the check-in date, length of stay, day of the week, meal plan, and sales channel. The standard 1C-Bitrix pricing mechanism (b_catalog_price) is designed for fixed price types, not for a "date × rate × length of stay" matrix. A custom model is required.
Rate Model
Rates are stored in the bl_room_rates table:
CREATE TABLE bl_room_rates (
id SERIAL PRIMARY KEY,
room_type_id INT NOT NULL,
rate_code VARCHAR(64) NOT NULL,
rate_name VARCHAR(255) NOT NULL,
meal_plan VARCHAR(20) DEFAULT 'RO', -- RO, BB, HB, FB
cancellation VARCHAR(20) DEFAULT 'free',-- free, non_refundable, 48h
min_nights SMALLINT DEFAULT 1,
max_nights SMALLINT,
active BOOLEAN DEFAULT true
);
Seasonal prices are stored in bl_room_rate_prices:
CREATE TABLE bl_room_rate_prices (
id SERIAL PRIMARY KEY,
rate_id INT REFERENCES bl_room_rates(id),
date_from DATE NOT NULL,
date_to DATE NOT NULL,
price_night NUMERIC(10,2) NOT NULL,
occupancy SMALLINT DEFAULT 2, -- for how many guests (1, 2, 3...)
day_mask SMALLINT DEFAULT 127 -- bitmask of days: Mon=1, Tue=2, Wed=4...
);
CREATE INDEX idx_rate_prices_dates ON bl_room_rate_prices(rate_id, date_from, date_to);
day_mask allows setting different prices for weekends and weekdays within a single season. Bit 64 = Sunday, 32 = Saturday, etc. Mask 127 = all days.
Calculating the Cost of Stay
For a given date range, the price for each night must be calculated individually (different nights may fall within different pricing periods) and then summed:
public function calculatePrice(int $rateId, \DateTime $dateFrom, \DateTime $dateTo, int $occupancy): float
{
$total = 0.0;
$current = clone $dateFrom;
while ($current < $dateTo) {
$dayBit = pow(2, (int)$current->format('N') - 1); // 1=Mon, 7=Sun
$priceRow = \Bitrix\Main\Application::getConnection()->query(
"SELECT price_night FROM bl_room_rate_prices
WHERE rate_id = ?
AND date_from <= ?
AND date_to > ?
AND occupancy <= ?
AND (day_mask & ?) > 0
ORDER BY occupancy DESC
LIMIT 1",
[$rateId, $current->format('Y-m-d'), $current->format('Y-m-d'), $occupancy, $dayBit]
)->fetch();
if (!$priceRow) {
throw new \RuntimeException('No price for date ' . $current->format('Y-m-d'));
}
$total += (float)$priceRow['price_night'];
$current->modify('+1 day');
}
return $total;
}
Minimum Stay Rules
Minimum and maximum night restrictions are often defined not at the rate level but for specific periods. The bl_room_min_stay table:
CREATE TABLE bl_room_min_stay (
room_type_id INT NOT NULL,
date_from DATE NOT NULL,
date_to DATE NOT NULL,
min_nights SMALLINT NOT NULL DEFAULT 1,
max_nights SMALLINT
);
During the New Year holidays, the minimum stay is 4 nights; at other times it is 1. When the booking form is rendered, the restriction is checked and a warning is displayed to the user if an invalid date range is selected.
Extra Guest Surcharges
The base price is calculated for two guests. A surcharge applies for the third and fourth guest. Stored in bl_room_rate_extra_guest:
| rate_id | guest_num | price_per_night |
|---|---|---|
| 1 | 3 | 800.00 |
| 1 | 4 | 800.00 |
When calculating the cost for 3 guests: base price + (number of nights × surcharge).
Administrative Pricing Interface
A "Rates & Prices" section is created in /bitrix/admin/. Key features:
- Rate list by room type with enable/disable toggle
- Price calendar — table with dates on the horizontal axis and rates on the vertical, inline editing by clicking a cell
- Period copy — copy last year's prices to the current year with an applied multiplier (e.g. ×1.1)
- Bulk update — change prices for a date range and a set of rates in a single operation
Integration with the Booking Form
On the website, the booking form sends an AJAX request to the RatesController::getAvailableAction controller when dates are selected. The controller:
- Checks room availability via
bl_room_booking - Loads available rates from
bl_room_rates - Calculates the price for each rate via
calculatePrice() - Returns a JSON array with options: rate, cancellation policy, meal plan, total price
The user sees multiple booking options and selects the most suitable one.
Timeline
| Phase | Duration |
|---|---|
| DB schema design and creation | 2 days |
| Price calculation class | 2 days |
| AJAX controller for form | 1 day |
| Administrative interface | 3–4 days |
| Edge case testing | 2 days |
| Total | 10–12 days |







