Setting up tariffs and seasonal prices for accommodation in 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
    1175
  • 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
    747
  • 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

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:

  1. Checks room availability via bl_room_booking
  2. Loads available rates from bl_room_rates
  3. Calculates the price for each rate via calculatePrice()
  4. 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