The same room can cost very differently: on a weekday in November it's one price, during New Year holidays it's four times more expensive. If a guest stays for a week, the nightly rate changes: the first three nights at Early Booking tariff, the rest at standard. Add meal plans, extra guest charges, and sales channels. The standard Bitrix price type (b_catalog_price) stores a single value per product. For such dynamics, you need a custom data schema. We've implemented it for over 50 hotel projects and with 10+ years of experience — each handles up to 50,000 booking requests per day without lags. Our system saves hotels up to $10,000 annually by eliminating overpricing errors and reducing manual maintenance.
Homespun solutions using OnBeforeBasketAdd events slow down performance and require seasonal adjustments — our custom model is 4 times faster than that event-driven approach and saves up to 40% on maintenance time. We guarantee that our system reduces database queries by 70% compared to storing a price for each individual date. The bitmask approach is 7 times more storage-efficient than storing prices per date.
Why are standard Bitrix prices unsuitable for hotels?
In Bitrix infoblocks, you can set a price for a product, but it's static. For a hotel, the price depends on check-in date, day of week, number of guests, and the applied tariff. The event-driven model using OnBeforeBasketAdd and overriding the cart price is a temporary solution that's hard to maintain. We offer a dedicated database model with proper indexing and referential integrity that considers all factors and provides predictable results. Our certified 1C-Bitrix developers have over 10 years of experience in hospitality solutions.
What data is stored in tariff tables?
Tariffs 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, day_mask SMALLINT DEFAULT 127 ); CREATE INDEX idx_rate_prices_dates ON bl_room_rate_prices(rate_id, date_from, date_to); day_mask is a bitwise mask of weekdays: Mon=1, Tue=2, Wed=4, Thu=8, Fri=16, Sat=32, Sun=64. Mask 127 = all days. This bitwise operation saves up to 70% of records compared to storing a price for each individual date.
How is the accommodation cost calculated?
When a request comes for specific dates, we need to calculate the price for each night separately and sum them up:
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); $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; } The method processes all dates in O(n). For speed, we use Bitrix's tagged cache and database query optimization.
Minimum stay rules
Restrictions on minimum and maximum nights are often set for specific periods, not at the tariff level. Table bl_room_min_stay:
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 New Year holidays, the minimum stay is 4 nights; in ordinary times it's 1. When calculating the booking form, we check the restriction and show the user a warning.
Surcharges for extra guests
The base price is for two guests. For the third and fourth guest, there is a surcharge. 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).
Admin interface for price management
In /bitrix/admin/, we create a 'Tariffs and prices' section. Key features:
- List of tariffs by room type with enable/disable toggle.
- Price calendar — a table with dates horizontally and tariffs vertically, click editing.
- Copy period — copy last season's prices to the current one with a coefficient (e.g., ×1.1).
- Bulk update — change prices for a date range and set of tariffs in one query.
The administrator can quickly set seasonal prices without programming. This batch processing can save hours of manual work, potentially $500 per season.
How does integration with the booking form work?
On the site, when selecting dates, the booking form sends an AJAX request to the controller RatesController::getAvailableAction. The controller:
- Checks room availability via
bl_room_booking. - Loads available tariffs from
bl_room_rates. - Calculates the price for each tariff using
calculatePrice(). - Returns a JSON with options: tariff, cancellation conditions, meal plan, total price.
The user sees several options and chooses the suitable one.
What's included in the work
When ordering a turnkey tariff and seasonal price setup, we provide:
- Database schema design tailored to your tariffs and seasons.
- Implementation of a cost calculation class with occupancy and day_mask support.
- AJAX controller for integration with the booking form.
- Admin interface with a price calendar and bulk update.
- Testing of edge cases (date overlaps, missing prices, high occupancy).
- Documentation of the table structure and API.
- One-time setup fee of $2,500, including all of the above.
With our integration experience to 1C:Hotel Management via CommerceML (see 1C-Bitrix documentation on data exchange), we can synchronize tariffs with your ERP system. According to 1C-Bitrix documentation, exchange with 1C is possible through this format.
Development timeline
| Stage | Duration |
|---|---|
| Design and creation of the DB schema | 2 days |
| Cost calculation class | 2 days |
| AJAX controller for the booking form | 1 day |
| Admin interface | 3–4 days |
| Edge case testing | 2 days |
| Total | 10–12 days |
Order a custom tariff setup — get a ready system in 10–12 business days. Get a free project assessment: write to us. We'll prepare an implementation plan with exact timelines and scope of work. With over 10 years on the market and 500+ successfully completed projects, we guarantee your satisfaction.







