Розробка форми розрахунку вартості на 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('uk-UA', {
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 днів |







