eppk-form/budget-dashboard.html

676 lines
27 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Дашборд исполнения бюджета — Казахтелеком</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet">
<style>
:root{--ink:#1a365d;--blue:#2563eb;--blue-light:#dbeafe;--white:#fff;--gray-500:#64748b;--gray-100:#f1f5f9;--green:#10B981;--red:#EF4444;--yellow:#F59E0B}
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:'Inter',sans-serif;font-size:15px;line-height:1.5;color:var(--ink);background:var(--gray-100)}
.header{background:var(--ink);color:var(--white);padding:24px 32px}
.header h1{font-size:24px;font-weight:800}
.header p{color:#9aa3b2;font-size:14px}
.container{max-width:1400px;margin:0 auto;padding:32px 24px}
.stats{display:grid;grid-template-columns:repeat(5,1fr);gap:16px;margin-bottom:32px}
.stat-card{background:var(--white);border-radius:12px;padding:20px}
.stat-card .label{font-size:13px;color:var(--gray-500);margin-bottom:4px}
.stat-card .value{font-size:28px;font-weight:800}
.stat-card .value.green{color:var(--green)}
.stat-card .value.yellow{color:var(--yellow)}
.stat-card .value.red{color:var(--red)}
.card{background:var(--white);border-radius:12px;padding:24px;margin-bottom:16px}
.card h2{font-size:18px;font-weight:700;margin-bottom:16px}
table{width:100%;border-collapse:collapse}
th,td{text-align:left;padding:12px 16px;border-bottom:1px solid var(--gray-100);font-size:14px}
th{font-weight:600;color:var(--gray-500);font-size:12px;text-transform:uppercase}
tr:hover{background:var(--blue-light)}
.btn{display:inline-block;padding:8px 16px;border-radius:6px;font-weight:600;font-size:13px;text-decoration:none;cursor:pointer;border:none}
.btn-primary{background:var(--blue);color:var(--white)}
.btn-primary:hover{background:#1d4ed8}
.btn-success{background:var(--green);color:var(--white)}
.btn-download{background:var(--gray-100);color:var(--ink)}
.filter-bar{display:flex;gap:12px;margin-bottom:16px;flex-wrap:wrap;align-items:center}
.filter-bar input,.filter-bar select{padding:8px 12px;border:2px solid var(--gray-100);border-radius:6px;font-size:14px}
.tabs{display:flex;gap:8px;margin-bottom:24px}
.tab{padding:10px 20px;border-radius:8px;font-weight:600;cursor:pointer;border:none;background:var(--white);color:var(--ink)}
.tab.active{background:var(--blue);color:var(--white)}
.progress-bar{height:8px;background:var(--gray-100);border-radius:4px;overflow:hidden;margin-top:8px}
.progress-fill{height:100%;border-radius:4px;transition:width 0.3s}
.progress-fill.good{background:var(--green)}
.progress-fill.warning{background:var(--yellow)}
.progress-fill.danger{background:var(--red)}
.text-right{text-align:right}
.text-center{text-align:center}
.total-row{background:var(--blue-light);font-weight:700}
.hidden{display:none}
@media(max-width:1024px){
.stats{grid-template-columns:repeat(2,1fr)}
}
@media(max-width:640px){
.stats{grid-template-columns:1fr}
table{font-size:12px}
th,td{padding:8px}
}
</style>
</head>
<body>
<div class="header">
<h1>Дашборд исполнения бюджета</h1>
<p>Мониторинг бюджета по статьям — филиалы Казахтелеком</p>
</div>
<div class="container">
<div class="tabs">
<button class="tab active" onclick="showTab('summary')">Сводная таблица</button>
<button class="tab" onclick="showTab('by-branch')">По филиалам</button>
<button class="tab" onclick="showTab('by-article')">По статьям</button>
<button class="tab" onclick="showTab('plan')">Ввод плана</button>
</div>
<!-- Сводная таблица -->
<div id="tab-summary">
<div class="stats">
<div class="stat-card">
<div class="label">Общий план</div>
<div class="value" id="total-plan">0</div>
</div>
<div class="stat-card">
<div class="label">Общий факт</div>
<div class="value" id="total-fact">0</div>
</div>
<div class="stat-card">
<div class="label">Выполнение</div>
<div class="value green" id="total-percent">0%</div>
</div>
<div class="stat-card">
<div class="label">Остаток</div>
<div class="value yellow" id="total-remain">0</div>
</div>
<div class="stat-card">
<div class="label">Филиалов сдано</div>
<div class="value green" id="submitted-branches">0</div>
</div>
</div>
<div class="card">
<div class="filter-bar">
<select id="month-filter" onchange="filterData()">
<option value="all">Все месяцы</option>
<option value="Январь">Январь</option>
<option value="Февраль">Февраль</option>
<option value="Март">Март</option>
<option value="Апрель">Апрель</option>
<option value="Май">Май</option>
<option value="Июнь">Июнь</option>
<option value="Июль">Июль</option>
<option value="Август">Август</option>
<option value="Сентябрь">Сентябрь</option>
<option value="Октябрь">Октябрь</option>
<option value="Ноябрь">Ноябрь</option>
<option value="Декабрь">Декабрь</option>
</select>
<select id="article-filter" onchange="filterData()">
<option value="all">Все статьи</option>
<option value="2.4.5">2.4.5 - Командировочные расходы</option>
<option value="2.4.6">2.4.6 - Подготовка кадров</option>
<option value="4.1.6">4.1.6 - Командировочные расходы</option>
<option value="4.1.7">4.1.7 - Подготовка кадров</option>
</select>
<button class="btn btn-download" onclick="exportToExcel()">Скачать Excel</button>
<button class="btn btn-primary" onclick="addTestData()">Добавить тестовые данные</button>
</div>
<table id="summary-table">
<thead>
<tr>
<th>Статья</th>
<th class="text-right">План (тенге)</th>
<th class="text-right">Факт (тенге)</th>
<th class="text-right">Нарастающий итог</th>
<th class="text-center">Выполнение</th>
<th>Прогресс</th>
</tr>
</thead>
<tbody id="summary-body"></tbody>
<tfoot>
<tr class="total-row">
<td><strong>ИТОГО</strong></td>
<td class="text-right" id="foot-plan">0</td>
<td class="text-right" id="foot-fact">0</td>
<td class="text-right" id="foot-cumulative">0</td>
<td class="text-center" id="foot-percent">0%</td>
<td></td>
</tr>
</tfoot>
</table>
</div>
</div>
<!-- По филиалам -->
<div id="tab-by-branch" class="hidden">
<div class="card">
<div class="filter-bar">
<input type="text" id="search-branch" placeholder="Поиск филиала..." oninput="filterBranchTable()">
<button class="btn btn-download" onclick="exportBranchesToExcel()">Скачать Excel</button>
</div>
<table id="branch-table">
<thead>
<tr>
<th>Филиал</th>
<th class="text-right">План (тенге)</th>
<th class="text-right">Факт (тенге)</th>
<th class="text-center">Выполнение</th>
<th>Прогресс</th>
<th>Статус</th>
</tr>
</thead>
<tbody id="branch-body"></tbody>
</table>
</div>
</div>
<!-- По статьям -->
<div id="tab-by-article" class="hidden">
<div class="card">
<div class="filter-bar">
<select id="article-detail-filter" onchange="filterArticleTable()">
<option value="all">Все статьи</option>
<option value="2.4.5">2.4.5 - Командировочные расходы</option>
<option value="2.4.6">2.4.6 - Подготовка кадров</option>
<option value="4.1.6">4.1.6 - Командировочные расходы</option>
<option value="4.1.7">4.1.7 - Подготовка кадров</option>
</select>
<button class="btn btn-download" onclick="exportArticlesToExcel()">Скачать Excel</button>
</div>
<table id="article-table">
<thead>
<tr>
<th>Статья</th>
<th>Филиал</th>
<th class="text-right">План (тенге)</th>
<th class="text-right">Факт (тенге)</th>
<th class="text-right">Нарастающий итог</th>
<th class="text-center">Выполнение</th>
</tr>
</thead>
<tbody id="article-body"></tbody>
</table>
</div>
</div>
<!-- Ввод плана -->
<div id="tab-plan" class="hidden">
<div class="card">
<h2>Ввод плановых показателей по месяцам</h2>
<p style="color:var(--gray-500);margin-bottom:24px">Введите плановые показатели для каждой статьи по месяцам. Данные сохраняются автоматически.</p>
<div class="filter-bar">
<select id="plan-month" onchange="renderPlanTable()">
<option value="Январь">Январь</option>
<option value="Февраль">Февраль</option>
<option value="Март">Март</option>
<option value="Апрель">Апрель</option>
<option value="Май">Май</option>
<option value="Июнь" selected>Июнь</option>
<option value="Июль">Июль</option>
<option value="Август">Август</option>
<option value="Сентябрь">Сентябрь</option>
<option value="Октябрь">Октябрь</option>
<option value="Ноябрь">Ноябрь</option>
<option value="Декабрь">Декабрь</option>
</select>
<select id="plan-branch" onchange="renderPlanTable()">
<option value="all">Все филиалы (сводный)</option>
${branches.map(b => `<option value="${b}">${b}</option>`).join('')}
</select>
<button class="btn btn-success" onclick="savePlanData()">Сохранить</button>
<button class="btn btn-download" onclick="exportPlanToExcel()">Скачать план Excel</button>
</div>
<table id="plan-table">
<thead>
<tr>
<th>Статья бюджета</th>
<th class="text-right">План на месяц (тенге)</th>
<th class="text-right">План на год (тенге)</th>
</tr>
</thead>
<tbody id="plan-body"></tbody>
<tfoot>
<tr class="total-row">
<td><strong>ИТОГО</strong></td>
<td class="text-right" id="plan-total-month">0</td>
<td class="text-right" id="plan-total-year">0</td>
</tr>
</tfoot>
</table>
</div>
<div class="card">
<h2>Помесячный план (свод)</h2>
<div style="overflow-x:auto">
<table id="monthly-plan-table">
<thead>
<tr>
<th>Статья</th>
<th class="text-right">Янв</th>
<th class="text-right">Фев</th>
<th class="text-right">Мар</th>
<th class="text-right">Апр</th>
<th class="text-right">Май</th>
<th class="text-right">Июн</th>
<th class="text-right">Июл</th>
<th class="text-right">Авг</th>
<th class="text-right">Сен</th>
<th class="text-right">Окт</th>
<th class="text-right">Ноя</th>
<th class="text-right">Дек</th>
<th class="text-right">Итого</th>
</tr>
</thead>
<tbody id="monthly-plan-body"></tbody>
</table>
</div>
</div>
</div>
</div>
<script>
const articles = [
{code: '2.4.5', name: '2.4.5 - Командировочные расходы по подготовке кадров и повышению квалификации кадров'},
{code: '2.4.6', name: '2.4.6 - Подготовка и повышение квалификации кадров'},
{code: '4.1.6', name: '4.1.6 - Командировочные расходы по подготовке кадров и повышению квалификации кадров'},
{code: '4.1.7', name: '4.1.7 - Подготовка и повышение квалификации кадров'}
];
const branches = [
"Астана","Алматы","Шымкент","Атырау","Актобе","Караганда","Павлодар",
"Петропавловск","Костанай","Туркестан","Усть-Каменогорск","Уральск",
"Семей","Кызылорда","Тараз","Жезказган","Экибастуз","Темиртау",
"Балхаш","Турксиб","ДРБ","ДКБ","ОДС","СФ","КУ","ДТК","ДУП","ДПБ","ДЦБ",
"ЦА (Центральный аппарат)"
];
let budgetData = JSON.parse(localStorage.getItem('budget-data')) || [];
function saveData() {
localStorage.setItem('budget-data', JSON.stringify(budgetData));
}
function addTestData() {
budgetData = [];
branches.forEach(branch => {
articles.forEach(article => {
const plan = Math.floor(Math.random() * 5000000) + 1000000;
const fact = Math.floor(plan * (0.5 + Math.random() * 0.6));
budgetData.push({
branch: branch,
month: 'Июнь',
year: '2026',
article: article.name,
articleCode: article.code,
plan: plan,
fact: fact,
cumulative: fact
});
});
});
saveData();
renderAll();
}
function renderAll() {
renderSummary();
renderBranches();
renderArticles();
updateStats();
}
function updateStats() {
let totalPlan = 0, totalFact = 0;
budgetData.forEach(item => {
totalPlan += item.plan;
totalFact += item.fact;
});
const percent = totalPlan > 0 ? Math.round((totalFact / totalPlan) * 100) : 0;
const remain = totalPlan - totalFact;
document.getElementById('total-plan').textContent = totalPlan.toLocaleString('ru-RU');
document.getElementById('total-fact').textContent = totalFact.toLocaleString('ru-RU');
document.getElementById('total-percent').textContent = percent + '%';
document.getElementById('total-remain').textContent = remain.toLocaleString('ru-RU');
const uniqueBranches = [...new Set(budgetData.map(d => d.branch))];
document.getElementById('submitted-branches').textContent = uniqueBranches.length;
}
function renderSummary() {
const monthFilter = document.getElementById('month-filter').value;
const articleFilter = document.getElementById('article-filter').value;
let filtered = budgetData;
if (monthFilter !== 'all') filtered = filtered.filter(d => d.month === monthFilter);
if (articleFilter !== 'all') filtered = filtered.filter(d => d.articleCode === articleFilter);
const summary = {};
filtered.forEach(item => {
if (!summary[item.articleCode]) {
summary[item.articleCode] = {article: item.article, plan: 0, fact: 0, cumulative: 0};
}
summary[item.articleCode].plan += item.plan;
summary[item.articleCode].fact += item.fact;
summary[item.articleCode].cumulative += item.cumulative;
});
const tbody = document.getElementById('summary-body');
tbody.innerHTML = '';
let totalPlan = 0, totalFact = 0, totalCum = 0;
Object.values(summary).forEach(item => {
const percent = item.plan > 0 ? Math.round((item.fact / item.plan) * 100) : 0;
const progressClass = percent >= 90 ? 'good' : percent >= 70 ? 'warning' : 'danger';
totalPlan += item.plan;
totalFact += item.fact;
totalCum += item.cumulative;
const row = document.createElement('tr');
row.innerHTML = `
<td>${item.article}</td>
<td class="text-right">${item.plan.toLocaleString('ru-RU')}</td>
<td class="text-right">${item.fact.toLocaleString('ru-RU')}</td>
<td class="text-right">${item.cumulative.toLocaleString('ru-RU')}</td>
<td class="text-center">${percent}%</td>
<td><div class="progress-bar"><div class="progress-fill ${progressClass}" style="width:${percent}%"></div></div></td>
`;
tbody.appendChild(row);
});
const totalPercent = totalPlan > 0 ? Math.round((totalFact / totalPlan) * 100) : 0;
document.getElementById('foot-plan').textContent = totalPlan.toLocaleString('ru-RU');
document.getElementById('foot-fact').textContent = totalFact.toLocaleString('ru-RU');
document.getElementById('foot-cumulative').textContent = totalCum.toLocaleString('ru-RU');
document.getElementById('foot-percent').textContent = totalPercent + '%';
}
function renderBranches() {
const summary = {};
budgetData.forEach(item => {
if (!summary[item.branch]) {
summary[item.branch] = {plan: 0, fact: 0};
}
summary[item.branch].plan += item.plan;
summary[item.branch].fact += item.fact;
});
const tbody = document.getElementById('branch-body');
tbody.innerHTML = '';
Object.entries(summary).forEach(([branch, data]) => {
const percent = data.plan > 0 ? Math.round((data.fact / data.plan) * 100) : 0;
const progressClass = percent >= 90 ? 'good' : percent >= 70 ? 'warning' : 'danger';
const status = percent >= 90 ? 'В норме' : percent >= 70 ? 'Внимание' : 'Критично';
const statusClass = percent >= 90 ? 'good' : percent >= 70 ? 'warning' : 'danger';
const row = document.createElement('tr');
row.innerHTML = `
<td><strong>${branch}</strong></td>
<td class="text-right">${data.plan.toLocaleString('ru-RU')}</td>
<td class="text-right">${data.fact.toLocaleString('ru-RU')}</td>
<td class="text-center">${percent}%</td>
<td><div class="progress-bar"><div class="progress-fill ${progressClass}" style="width:${percent}%"></div></div></td>
<td><span class="btn btn-${statusClass}" style="font-size:12px">${status}</span></td>
`;
tbody.appendChild(row);
});
}
function renderArticles() {
const filter = document.getElementById('article-detail-filter').value;
let filtered = budgetData;
if (filter !== 'all') filtered = filtered.filter(d => d.articleCode === filter);
const tbody = document.getElementById('article-body');
tbody.innerHTML = '';
filtered.forEach(item => {
const percent = item.plan > 0 ? Math.round((item.fact / item.plan) * 100) : 0;
const row = document.createElement('tr');
row.innerHTML = `
<td>${item.article}</td>
<td>${item.branch}</td>
<td class="text-right">${item.plan.toLocaleString('ru-RU')}</td>
<td class="text-right">${item.fact.toLocaleString('ru-RU')}</td>
<td class="text-right">${item.cumulative.toLocaleString('ru-RU')}</td>
<td class="text-center">${percent}%</td>
`;
tbody.appendChild(row);
});
}
function filterData() {
renderSummary();
}
function filterBranchTable() {
const search = document.getElementById('search-branch').value.toLowerCase();
document.querySelectorAll('#branch-body tr').forEach(row => {
const text = row.textContent.toLowerCase();
row.style.display = text.includes(search) ? '' : 'none';
});
}
function filterArticleTable() {
renderArticles();
}
function showTab(tab) {
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
event.target.classList.add('active');
document.getElementById('tab-summary').classList.toggle('hidden', tab !== 'summary');
document.getElementById('tab-by-branch').classList.toggle('hidden', tab !== 'by-branch');
document.getElementById('tab-by-article').classList.toggle('hidden', tab !== 'by-article');
document.getElementById('tab-plan').classList.toggle('hidden', tab !== 'plan');
if (tab === 'plan') {
renderPlanTable();
renderMonthlyPlanTable();
}
}
function exportToExcel() {
let csv = '\uFEFF';
csv += 'Статья;План;Факт;Нарастающий итог;Выполнение (%)\n';
const monthFilter = document.getElementById('month-filter').value;
const articleFilter = document.getElementById('article-filter').value;
let filtered = budgetData;
if (monthFilter !== 'all') filtered = filtered.filter(d => d.month === monthFilter);
if (articleFilter !== 'all') filtered = filtered.filter(d => d.articleCode === articleFilter);
const summary = {};
filtered.forEach(item => {
if (!summary[item.articleCode]) {
summary[item.articleCode] = {article: item.article, plan: 0, fact: 0, cumulative: 0};
}
summary[item.articleCode].plan += item.plan;
summary[item.articleCode].fact += item.fact;
summary[item.articleCode].cumulative += item.cumulative;
});
let totalPlan = 0, totalFact = 0;
Object.values(summary).forEach(item => {
const percent = item.plan > 0 ? Math.round((item.fact / item.plan) * 100) : 0;
csv += `${item.article};${item.plan};${item.fact};${item.cumulative};${percent}\n`;
totalPlan += item.plan;
totalFact += item.fact;
});
const totalPercent = totalPlan > 0 ? Math.round((totalFact / totalPlan) * 100) : 0;
csv += `ИТОГО;${totalPlan};${totalFact};${totalFact};${totalPercent}\n`;
downloadCSV(csv, `budget_summary_${new Date().toISOString().slice(0,10)}.csv`);
}
function exportBranchesToExcel() {
let csv = '\uFEFF';
csv += 'Филиал;План;Факт;Выполнение (%);Статус\n';
const summary = {};
budgetData.forEach(item => {
if (!summary[item.branch]) {
summary[item.branch] = {plan: 0, fact: 0};
}
summary[item.branch].plan += item.plan;
summary[item.branch].fact += item.fact;
});
Object.entries(summary).forEach(([branch, data]) => {
const percent = data.plan > 0 ? Math.round((data.fact / data.plan) * 100) : 0;
const status = percent >= 90 ? 'В норме' : percent >= 70 ? 'Внимание' : 'Критично';
csv += `${branch};${data.plan};${data.fact};${percent};${status}\n`;
});
downloadCSV(csv, `budget_by_branch_${new Date().toISOString().slice(0,10)}.csv`);
}
function exportArticlesToExcel() {
let csv = '\uFEFF';
csv += 'Статья;Филиал;План;Факт;Нарастающий итог;Выполнение (%)\n';
const filter = document.getElementById('article-detail-filter').value;
let filtered = budgetData;
if (filter !== 'all') filtered = filtered.filter(d => d.articleCode === filter);
filtered.forEach(item => {
const percent = item.plan > 0 ? Math.round((item.fact / item.plan) * 100) : 0;
csv += `${item.article};${item.branch};${item.plan};${item.fact};${item.cumulative};${percent}\n`;
});
downloadCSV(csv, `budget_by_article_${new Date().toISOString().slice(0,10)}.csv`);
}
function downloadCSV(csv, filename) {
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = filename;
link.click();
}
// Plan functions
let planData = JSON.parse(localStorage.getItem('plan-data')) || {};
function savePlanData() {
const month = document.getElementById('plan-month').value;
const branch = document.getElementById('plan-branch').value;
const key = `${branch}-${month}`;
const rows = document.querySelectorAll('#plan-body tr');
planData[key] = {};
rows.forEach(row => {
const articleCode = row.getAttribute('data-code');
const monthPlan = parseInt(row.querySelector('.month-plan').value) || 0;
const yearPlan = parseInt(row.querySelector('.year-plan').value) || 0;
planData[key][articleCode] = {monthPlan, yearPlan};
});
localStorage.setItem('plan-data', JSON.stringify(planData));
alert('План сохранён!');
renderMonthlyPlanTable();
}
function renderPlanTable() {
const month = document.getElementById('plan-month').value;
const branch = document.getElementById('plan-branch').value;
const key = `${branch}-${month}`;
const data = planData[key] || {};
const tbody = document.getElementById('plan-body');
tbody.innerHTML = '';
let totalMonth = 0, totalYear = 0;
articles.forEach(article => {
const item = data[article.code] || {monthPlan: 0, yearPlan: 0};
totalMonth += item.monthPlan;
totalYear += item.yearPlan;
const row = document.createElement('tr');
row.setAttribute('data-code', article.code);
row.innerHTML = `
<td>${article.name}</td>
<td class="text-right"><input type="number" class="month-plan" value="${item.monthPlan}" style="width:150px;text-align:right;padding:8px;border:2px solid var(--gray-100);border-radius:6px"></td>
<td class="text-right"><input type="number" class="year-plan" value="${item.yearPlan}" style="width:150px;text-align:right;padding:8px;border:2px solid var(--gray-100);border-radius:6px"></td>
`;
tbody.appendChild(row);
});
document.getElementById('plan-total-month').textContent = totalMonth.toLocaleString('ru-RU');
document.getElementById('plan-total-year').textContent = totalYear.toLocaleString('ru-RU');
}
function renderMonthlyPlanTable() {
const months = ['Январь','Февраль','Март','Апрель','Май','Июнь','Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'];
const branch = document.getElementById('plan-branch').value;
const tbody = document.getElementById('monthly-plan-body');
tbody.innerHTML = '';
articles.forEach(article => {
const row = document.createElement('tr');
let total = 0;
let cells = `<td>${article.name}</td>`;
months.forEach(month => {
const key = `${branch}-${month}`;
const data = planData[key] || {};
const item = data[article.code] || {monthPlan: 0};
total += item.monthPlan;
cells += `<td class="text-right">${item.monthPlan.toLocaleString('ru-RU')}</td>`;
});
cells += `<td class="text-right"><strong>${total.toLocaleString('ru-RU')}</strong></td>`;
row.innerHTML = cells;
tbody.appendChild(row);
});
}
function exportPlanToExcel() {
const months = ['Январь','Февраль','Март','Апрель','Май','Июнь','Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'];
const branch = document.getElementById('plan-branch').value;
let csv = '\uFEFF';
csv += 'Статья;' + months.join(';') + ';Итого\n';
articles.forEach(article => {
let row = article.name;
let total = 0;
months.forEach(month => {
const key = `${branch}-${month}`;
const data = planData[key] || {};
const item = data[article.code] || {monthPlan: 0};
total += item.monthPlan;
row += ';' + item.monthPlan;
});
row += ';' + total;
csv += row + '\n';
});
downloadCSV(csv, `budget_plan_${branch}_${new Date().toISOString().slice(0,10)}.csv`);
}
renderAll();
</script>
</body>
</html>