Разработка формы расчета стоимости на 1С-Битрикс
Форма расчёта стоимости — калькулятор, который позволяет пользователю самостоятельно оценить цену услуги или продукта до звонка менеджеру. Применяется в строительстве, IT-услугах, производстве, логистике, страховании. Конверсионный эффект двоякий: часть пользователей уходит, увидев цену, — но оставшиеся приходят уже подготовленными и с реалистичными ожиданиями.
Типы калькуляторов
Простой (детерминированный): каждый параметр имеет фиксированную цену. Итог = сумма компонентов. Пример: выбор комплектации автомобиля.
Коэффициентный: есть базовая цена и коэффициенты — региональный, сезонный, по сложности. Итог = базовая_цена × К1 × К2 × ... Применяется в строительстве, страховании.
Формульный: произвольная формула с несколькими входными переменными. Пример: стоимость перевозки = вес × расстояние × тариф + фиксированная стоимость.
С диапазоном: результат не точная сумма, а диапазон «от X до Y». Применяется в IT-разработке, когда точный расчёт без ТЗ невозможен.
Хранение конфигурации калькулятора
Конфигурацию лучше хранить в базе (HL-блок), а не в коде — чтобы менеджеры могли менять цены без разработчика.
class CalculatorConfigTable extends \Bitrix\Main\ORM\Data\DataManager
{
public static function getTableName(): string { return 'b_hl_calculator_config'; }
public static function getMap(): array
{
return [
new IntegerField('ID', ['primary' => true, 'autocomplete' => true]),
new StringField('SLUG'), // Идентификатор калькулятора
new StringField('TITLE'),
new TextField('PARAMS_JSON'), // Параметры и их цены
new TextField('FORMULA_JSON'), // Формула расчёта
new StringField('CURRENCY'), // RUB, USD, EUR
new FloatField('MIN_PRICE'), // Минимальная цена
new FloatField('MAX_PRICE'), // NULL = без ограничения
new BooleanField('SHOW_RANGE', ['values' => [false, true]]),
new IntegerField('RANGE_PERCENT'), // ±% для диапазона
];
}
}
PARAMS_JSON — описание параметров:
[
{
"id": "area",
"label": "Площадь (кв.м.)",
"type": "range",
"min": 10,
"max": 500,
"default": 50,
"step": 5,
"unit": "кв.м."
},
{
"id": "floors",
"label": "Количество этажей",
"type": "select",
"options": [
{"value": 1, "label": "1 этаж", "price": 0},
{"value": 2, "label": "2 этажа", "price": 15000},
{"value": 3, "label": "3 этажа", "price": 35000}
]
},
{
"id": "foundation",
"label": "Тип фундамента",
"type": "radio",
"options": [
{"value": "tape", "label": "Ленточный", "price_per_m2": 2500},
{"value": "pile", "label": "Свайный", "price_per_m2": 1800},
{"value": "slab", "label": "Плитный", "price_per_m2": 4200}
]
},
{
"id": "finishing",
"label": "Отделка",
"type": "checkbox-group",
"options": [
{"value": "rough", "label": "Черновая", "price_per_m2": 3000},
{"value": "prefinish", "label": "Предчистовая","price_per_m2": 5500},
{"value": "finish", "label": "Чистовая", "price_per_m2": 9000}
]
}
]
JavaScript-движок расчёта
class PriceCalculator {
constructor(config) {
this.params = config.params;
this.formula = config.formula; // 'params_sum' | 'formula' | string
this.currency = config.currency;
this.minPrice = config.min_price;
this.showRange = config.show_range;
this.rangePercent = config.range_percent || 15;
this.values = {}; // Текущие значения параметров
this.initDefaults();
}
initDefaults() {
this.params.forEach(param => {
if (param.default !== undefined) {
this.values[param.id] = param.default;
} else if (param.type === 'select' || param.type === 'radio') {
this.values[param.id] = param.options[0]?.value;
} else if (param.type === 'checkbox-group') {
this.values[param.id] = [];
}
});
}
setValue(paramId, value) {
this.values[paramId] = value;
}
calculate() {
let total = 0;
const area = parseFloat(this.values['area']) || 1;
this.params.forEach(param => {
const val = this.values[param.id];
if (!val && val !== 0) return;
switch (param.type) {
case 'range':
case 'number':
// Базовая цена за единицу (если задана)
if (param.price_per_unit) {
total += parseFloat(val) * param.price_per_unit;
}
break;
case 'select':
case 'radio': {
const opt = param.options.find(o => String(o.value) === String(val));
if (opt) {
total += (opt.price || 0) + (opt.price_per_m2 || 0) * area;
}
break;
}
case 'checkbox-group': {
const selected = Array.isArray(val) ? val : [val];
selected.forEach(v => {
const opt = param.options.find(o => String(o.value) === String(v));
if (opt) {
total += (opt.price || 0) + (opt.price_per_m2 || 0) * area;
}
});
break;
}
}
});
if (this.minPrice && total < this.minPrice) {
total = this.minPrice;
}
if (this.showRange) {
const delta = total * (this.rangePercent / 100);
return {
min: Math.floor(total - delta),
max: Math.ceil(total + delta),
exact: null,
};
}
return {min: null, max: null, exact: total};
}
formatPrice(value) {
return new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: this.currency,
maximumFractionDigits: 0,
}).format(value);
}
}
Интеграция с формой заявки
После расчёта — кнопка «Оставить заявку» открывает форму с предзаполненным полем расчёта:
document.getElementById('btn-get-quote').addEventListener('click', () => {
const result = calculator.calculate();
const resultText = result.exact
? calculator.formatPrice(result.exact)
: `от ${calculator.formatPrice(result.min)} до ${calculator.formatPrice(result.max)}`;
// Заполнить скрытые поля формы
document.getElementById('form-calc-result').value = resultText;
document.getElementById('form-calc-params').value = JSON.stringify(calculator.values);
// Прокрутить к форме или открыть модальное окно
document.getElementById('quote-form').scrollIntoView({behavior: 'smooth'});
});
Обработчик на сервере сохраняет параметры расчёта в комментарии лида:
$calcParams = json_decode($data['calc_params'] ?? '{}', true);
$paramsSummary = [];
foreach ($calcParams as $paramId => $value) {
$paramsSummary[] = $paramId . ': ' . (is_array($value) ? implode(', ', $value) : $value);
}
$lead = new \CCrmLead(false);
$lead->Add([
'TITLE' => 'Расчёт стоимости — ' . $name,
'COMMENTS' => "Результат расчёта: {$calcResult}\n" . implode("\n", $paramsSummary),
]);
Сохранение расчётов
Полезно сохранять расчёты для аналитики — какие параметры выбирают чаще, какие значения приводят к заявке:
class CalculatorLeadTable extends \Bitrix\Main\ORM\Data\DataManager
{
public static function getTableName(): string { return 'b_hl_calc_leads'; }
// CALC_ID, PARAMS_JSON, RESULT_MIN, RESULT_MAX, RESULT_EXACT, SESSION_ID, CONVERTED (bool), CREATED_AT
}
Записывать при каждом нажатии «Рассчитать», помечать CONVERTED = true при создании лида. Процент конверсии = CONVERTED / всего записей.
Сроки разработки
| Вариант | Состав | Срок |
|---|---|---|
| Простой калькулятор | Фиксированные цены, итог, форма | 3–5 дней |
| Коэффициентный | Базовая цена × коэффициенты, диапазон | 5–8 дней |
| С конфигуратором | Управление ценами через административную панель | 8–14 дней |







