Development of a Service Cost Calculator on 1C-Bitrix
A service cost calculator is a qualification tool: it answers the question "how much does this cost" at the moment when a potential client is not yet ready to call, but is already ready to make a decision. A well-built calculator does not promise an exact price — it gives a range sufficient for the client to understand whether it fits their budget.
Specifics of service cost calculation
Services are more complex than goods: they have no fixed price. The cost depends on volume, complexity, deadlines, contractor qualifications, region, and seasonality. The calculator must account for these variables without turning into an endless questionnaire.
Typical parameters for a service calculator:
- Volume of work — area, number of units, number of hours
- Type/category — basic, standard, premium
- Additional options — urgency, specialist on-site visit, materials
- Geography — region, distance
Each parameter affects the total sum through a coefficient or a fixed surcharge.
Data model: rates in a HighLoad block
The rate table for a service calculator is stored in a HighLoad block — this is more performant than an info block and more convenient than hard-coded arrays:
// Fetching rates from an HL block
$hlblock = \Bitrix\Highloadblock\HighloadBlockTable::getById(SERVICES_TARIFF_HL_ID)->fetch();
$entity = \Bitrix\Highloadblock\HighloadBlockTable::compileEntity($hlblock);
$className = $entity->getDataClass();
$tariffs = $className::getList([
'select' => ['UF_SERVICE_TYPE', 'UF_VOLUME_FROM', 'UF_VOLUME_TO', 'UF_BASE_PRICE', 'UF_PRICE_PER_UNIT'],
'filter' => ['=UF_ACTIVE' => true],
'order' => ['UF_SERVICE_TYPE' => 'ASC', 'UF_VOLUME_FROM' => 'ASC'],
])->fetchAll();
HL table structure:
| Field | Type | Purpose |
|---|---|---|
UF_SERVICE_TYPE |
Reference | Service type |
UF_VOLUME_FROM |
Float | Minimum volume |
UF_VOLUME_TO |
Float | Maximum volume |
UF_BASE_PRICE |
Float | Base cost |
UF_PRICE_PER_UNIT |
Float | Cost per unit above the base |
UF_COMPLEXITY_COEFFICIENT |
Float | Complexity coefficient |
Calculation logic in PHP
A service class encapsulating the calculation algorithm:
namespace MyProject\Services;
class ServiceCostCalculator
{
private array $tariffs;
private array $options;
public function __construct(array $tariffs, array $options)
{
$this->tariffs = $tariffs;
$this->options = $options;
}
public function calculate(string $serviceType, float $volume, array $extras = []): array
{
$tariff = $this->findTariff($serviceType, $volume);
if (!$tariff) {
throw new \InvalidArgumentException("Tariff not found for volume {$volume}");
}
$baseCost = $tariff['UF_BASE_PRICE'];
if ($volume > $tariff['UF_VOLUME_FROM']) {
$extra = $volume - $tariff['UF_VOLUME_FROM'];
$baseCost += $extra * $tariff['UF_PRICE_PER_UNIT'];
}
// Apply coefficients of additional options
$optionsCost = 0;
foreach ($extras as $optionKey => $enabled) {
if ($enabled && isset($this->options[$optionKey])) {
$opt = $this->options[$optionKey];
if ($opt['type'] === 'percent') {
$optionsCost += $baseCost * ($opt['value'] / 100);
} else {
$optionsCost += $opt['value'];
}
}
}
$total = $baseCost + $optionsCost;
return [
'base_cost' => round($baseCost, 2),
'options_cost' => round($optionsCost, 2),
'total_min' => round($total * 0.9, 2), // -10% — lower bound
'total_max' => round($total * 1.15, 2), // +15% — upper bound
'total' => round($total, 2),
'currency' => 'RUB',
];
}
private function findTariff(string $serviceType, float $volume): ?array
{
foreach ($this->tariffs as $tariff) {
if ($tariff['UF_SERVICE_TYPE'] === $serviceType
&& $volume >= $tariff['UF_VOLUME_FROM']
&& $volume <= $tariff['UF_VOLUME_TO']) {
return $tariff;
}
}
return null;
}
}
Displaying a price range
A fixed price for services is often impossible. The calculator shows a range:
function updateResult(data) {
const formatPrice = (p) => new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB',
maximumFractionDigits: 0,
}).format(p);
document.getElementById('result-min').textContent = formatPrice(data.total_min);
document.getElementById('result-max').textContent = formatPrice(data.total_max);
document.getElementById('result-note').textContent =
'Exact cost is determined after an on-site inspection';
}
CRM integration: passing the calculation to a deal
After the user submits a request, the calculation data is added to a deal or CRM lead:
// Custom lead fields to store calculation parameters
$lead->Add([
'TITLE' => 'Calculation: ' . $serviceTypeName,
'OPPORTUNITY' => $calculationResult['total'],
'CURRENCY_ID' => 'RUB',
'COMMENTS' => $this->buildCommentsFromParams($params, $calculationResult),
'UF_CALC_PARAMS' => json_encode($params), // JSON of parameters
'UF_CALC_RESULT' => json_encode($calculationResult),
]);
Timelines
| Task | Timeline |
|---|---|
| Calculator with 5–7 parameters, linear calculation, request form | 5–8 days |
| Calculator with a rate grid from an HL block, price range, CRM integration | 2–3 weeks |
| Multi-service calculator with service selection, complex coefficients, and calculation history | 4–6 weeks |
A good service cost calculator is not a price promise — it is a budget qualification tool. The client understands the ballpark figure and makes a decision about the next step. The task is to make that step as easy as possible.







