// Данные для расчёта let calculationData = {}; // Форматирование числа в тенге function formatTenge(num) { return new Intl.NumberFormat('kk-KZ', { style: 'currency', currency: 'KZT', minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(num); } // Расчёт стоимости function calculate() { // Параметры клиента const users = parseInt(document.getElementById('users').value) || 0; const term = parseInt(document.getElementById('term').value) || 1; const serviceType = document.getElementById('serviceType').value; const minutes = parseInt(document.getElementById('minutes').value) || 0; const dataGb = parseInt(document.getElementById('dataGb').value) || 0; const addons = document.getElementById('addons').value; // Затраты компании const costMinute = parseFloat(document.getElementById('costMinute').value) || 0; const costGb = parseFloat(document.getElementById('costGb').value) || 0; const opex = parseFloat(document.getElementById('opex').value) || 0; const margin = parseFloat(document.getElementById('margin').value) || 0; let volumeDiscount = parseFloat(document.getElementById('volumeDiscount').value) || 0; let termDiscount = parseFloat(document.getElementById('termDiscount').value) || 0; // Автоматические скидки if (users >= 100) { volumeDiscount = Math.max(volumeDiscount, 10); } if (term >= 12) { termDiscount = Math.max(termDiscount, 5); } if (term >= 24) { termDiscount = Math.max(termDiscount, 10); } // Расчёт себестоимости на пользователя в месяц let serviceCost = 0; switch (serviceType) { case 'mobile': serviceCost = (minutes * costMinute) + opex; break; case 'internet': serviceCost = (dataGb * costGb) + opex; break; case 'combined': serviceCost = (minutes * costMinute) + (dataGb * costGb) + opex; break; case 'cloud': serviceCost = (dataGb * costGb * 1.5) + opex; // Облака дороже break; } // Добавочная стоимость за сервисы let addonCost = 0; switch (addons) { case 'basic': addonCost = 500; break; case 'advanced': addonCost = 1200; break; case 'premium': addonCost = 2500; break; } const totalCostPerUser = serviceCost + addonCost; // Расчёт цены с маржой const priceWithMargin = totalCostPerUser * (1 + margin / 100); // Применение скидок const totalDiscount = Math.min(volumeDiscount + termDiscount, 40); // Максимум 40% const finalPrice = priceWithMargin * (1 - totalDiscount / 100); // Данные конкурентов const competitors = [ { name: 'Kazakhtelecom', base: parseFloat(document.getElementById('comp1Base').value) || 0, biz: parseFloat(document.getElementById('comp1Biz').value) || 0, prem: parseFloat(document.getElementById('comp1Prem').value) || 0 }, { name: 'Kcell/Altel', base: parseFloat(document.getElementById('comp2Base').value) || 0, biz: parseFloat(document.getElementById('comp2Biz').value) || 0, prem: parseFloat(document.getElementById('comp2Prem').value) || 0 }, { name: 'Beeline', base: parseFloat(document.getElementById('comp3Base').value) || 0, biz: parseFloat(document.getElementById('comp3Biz').value) || 0, prem: parseFloat(document.getElementById('comp3Prem').value) || 0 }, { name: 'Tele2', base: parseFloat(document.getElementById('comp4Base').value) || 0, biz: parseFloat(document.getElementById('comp4Biz').value) || 0, prem: parseFloat(document.getElementById('comp4Prem').value) || 0 } ]; // Средние цены конкурентов const avgCompetitors = competitors.reduce((sum, c) => sum + c.biz, 0) / competitors.length; // Итого const totalMonthly = finalPrice * users; const totalContract = totalMonthly * term; // Сохранение данных calculationData = { users, term, serviceType, minutes, dataGb, addons, costMinute, costGb, opex, margin, volumeDiscount, termDiscount, totalCostPerUser, finalPrice, avgCompetitors, totalMonthly, totalContract, competitors }; // Вывод результатов document.getElementById('resultCost').textContent = formatTenge(totalCostPerUser); document.getElementById('resultPrice').textContent = formatTenge(finalPrice); document.getElementById('resultCompetitor').textContent = formatTenge(avgCompetitors); document.getElementById('resultTotal').textContent = formatTenge(totalMonthly); document.getElementById('resultContract').textContent = formatTenge(totalContract); // Конкурентное преимущество const advantage = ((avgCompetitors - finalPrice) / avgCompetitors * 100).toFixed(1); const advantageEl = document.getElementById('resultAdvantage'); if (advantage > 0) { advantageEl.textContent = `+${advantage}% дешевле конкурентов`; advantageEl.style.color = '#48bb78'; } else if (advantage < 0) { advantageEl.textContent = `${advantage}% дороже конкурентов`; advantageEl.style.color = '#f56565'; } else { advantageEl.textContent = 'На уровне рынка'; advantageEl.style.color = '#666'; } // Отрисовка сравнения renderComparison(finalPrice, competitors); // Рекомендации renderRecommendations(finalPrice, avgCompetitors, margin, totalDiscount); } // Отрисовка сравнения с конкурентами function renderComparison(ourPrice, competitors) { const container = document.getElementById('comparisonBars'); const maxValue = Math.max(ourPrice, ...competitors.map(c => c.biz)) * 1.2; let html = `
Наш тариф
${formatTenge(ourPrice)}
${formatTenge(ourPrice)}
`; const compClasses = ['comp1', 'comp2', 'comp3', 'comp4']; competitors.forEach((comp, i) => { html += `
${comp.name}
${formatTenge(comp.biz)}
${formatTenge(comp.biz)}
`; }); container.innerHTML = html; } // Рекомендации function renderRecommendations(price, avgCompetitorPrice, margin, discount) { const container = document.getElementById('recommendations'); const recommendations = []; if (price > avgCompetitorPrice * 1.1) { recommendations.push('Цена выше средней по рынку на 10%+. Рассмотрите возможность снижения маржинальности или увеличения скидок.'); } else if (price < avgCompetitorPrice * 0.9) { recommendations.push('Цена ниже рынка — можно увеличить маржу на 5-10% без потери конкурентоспособности.'); } else { recommendations.push('Цена находится в рыночном диапазоне — оптимально для привлечения клиентов.'); } if (margin < 15) { recommendations.push('Низкая маржинальность. Убедитесь, что операционные расходы учтены полностью.'); } else if (margin > 40) { recommendations.push('Высокая маржинальность. Проверьте, соответствует ли ценность услуги такой наценке.'); } if (discount === 0) { recommendations.push('Скидки не применяются. Рассмотрите возможность предоставления скидки за объём или срок.'); } if (discount >= 30) { recommendations.push('Высокий уровень скидок. Убедитесь, что маржинальность остаётся приемлемой.'); } const status = price <= avgCompetitorPrice ? 'Конкурентоспособно' : 'Требуется оптимизация'; const statusColor = price <= avgCompetitorPrice ? '#48bb78' : '#f56565'; container.innerHTML = `

💡 Рекомендации (${status})

`; } // Экспорт в Excel (CSV с BOM) function exportToExcel() { if (!calculationData.finalPrice) { alert('Сначала выполните расчёт!'); return; } const d = calculationData; // CSV с BOM для корректной кириллицы const bom = '\uFEFF'; const separator = ';'; const rows = [ ['ПАРАМЕТРЫ КЛИЕНТА'], ['Количество пользователей', d.users], ['Срок подключения (месяцев)', d.term], ['Тип услуги', d.serviceType], ['Объём минут', d.minutes], ['Объём интернета (ГБ)', d.dataGb], ['Дополнительные сервисы', d.addons], [], ['ЗАТРАТЫ КОМПАНИИ'], ['Себестоимость минуты (₸)', d.costMinute], ['Себестоимость 1 ГБ (₸)', d.costGb], ['Операционные расходы (₸)', d.opex], ['Маржинальность (%)', d.margin], ['Скидка за объём (%)', d.volumeDiscount], ['Скидка за срок (%)', d.termDiscount], [], ['РЕЗУЛЬТАТЫ РАСЧЁТА'], ['Себестоимость на пользователя (₸)', d.totalCostPerUser.toFixed(2)], ['Рекомендованная цена (₸)', d.finalPrice.toFixed(2)], ['Средняя цена конкурентов (₸)', d.avgCompetitors.toFixed(2)], ['Итого в месяц (₸)', d.totalMonthly.toFixed(2)], ['Итого за контракт (₸)', d.totalContract.toFixed(2)], [], ['КОНКУРЕНТЫ (Бизнес тариф)'], ['Kazakhtelecom', d.competitors[0].biz], ['Kcell/Altel', d.competitors[1].biz], ['Beeline', d.competitors[2].biz], ['Tele2', d.competitors[3].biz] ]; const csv = rows.map(row => row.map(cell => { const str = String(cell ?? ''); return '"' + str.replace(/"/g, '""') + '"'; }).join(separator) ).join('\n'); const blob = new Blob([bom + csv], { type: 'text/csv;charset=utf-8;' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `tarif_${new Date().toISOString().slice(0,10)}.csv`; a.click(); URL.revokeObjectURL(url); } // Event listeners document.getElementById('calculateBtn').addEventListener('click', calculate); document.getElementById('exportBtn').addEventListener('click', exportToExcel); // Автоматический расчёт при загрузке window.addEventListener('DOMContentLoaded', calculate); // Пересчёт при изменении полей document.querySelectorAll('input, select').forEach(el => { el.addEventListener('change', calculate); });