diff --git a/index.html b/index.html index 7ad4aa9..50dce4b 100644 --- a/index.html +++ b/index.html @@ -78,7 +78,7 @@
πŸ“ˆ

Π”Π°Π½Π½Ρ‹Π΅

-

Код ΡΡ‚Π°Ρ‚ΡŒΠΈ Π±ΡŽΠ΄ΠΆΠ΅Ρ‚Π°, Π½Π°ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Π½ΠΈΠ΅ услуг/Ρ€Π°Π±ΠΎΡ‚, ΠΏΠΎΠ΄Ρ€Π°Π·Π΄Π΅Π»Π΅Π½ΠΈΠ΅-Π·Π°ΠΊΠ°Π·Ρ‡ΠΈΠΊ, ΠΏΠ»Π°Π½ΠΎΠ²Ρ‹Π΅ суммы ΠΈ фактичСскоС исполнСниС

+

ΠšΡ€Π°Ρ‚ΠΊΠΈΠΉ тСкст ΠΌΠ°Ρ‚Π΅Ρ€ΠΈΠ°Π»Π°, ΠΏΠΎΠ΄Ρ€Π°Π·Π΄Π΅Π»Π΅Π½ΠΈΠ΅ (ΠœΠ’Π—), Ρ‚ΠΈΠΏ Π±ΡŽΠ΄ΠΆΠ΅Ρ‚Π° (Π‘ΠšΠ’/ΠžΠ‘), Π±ΡŽΠ΄ΠΆΠ΅Ρ‚ Π·Π°ΠΊΡƒΠΏΠΎΠΊ, сумма поставки, экономия

🎯
@@ -117,11 +117,12 @@ - - + - - + + + + @@ -180,14 +181,19 @@ function renderStats() { const totalBudget = allData.reduce((sum, r) => sum + (r.budgetSum || 0), 0); const totalExecuted = allData.reduce((sum, r) => sum + (r.executedSum || 0), 0); + const totalEconomy = allData.reduce((sum, r) => sum + (r.economy || 0), 0); const avgPercent = totalBudget > 0 ? (totalExecuted / totalBudget) * 100 : 0; const fullyExecuted = allData.filter(r => r.execPercent >= 95).length; + const bkvCount = allData.filter(r => r.budgetType === 'Π‘ΠšΠ’').length; + const obCount = allData.filter(r => r.budgetType === 'ΠžΠ‘').length; document.getElementById('stats').innerHTML = - '
' + formatAmount(totalBudget) + '
ВсСго Π±ΡŽΠ΄ΠΆΠ΅Ρ‚
' + - '
' + formatAmount(totalExecuted) + '
ИсполнСно
' + + '
' + formatAmount(totalBudget) + '
Π‘ΡŽΠ΄ΠΆΠ΅Ρ‚ Π·Π°ΠΊΡƒΠΏΠΎΠΊ, Ρ‚Π³
' + + '
' + formatAmount(totalExecuted) + '
Π‘ΡƒΠΌΠΌΠ° поставки, Ρ‚Π³
' + + '
' + formatAmount(totalEconomy) + '
Экономия, Ρ‚Π³
' + '
' + formatAmount(avgPercent) + '%
Π‘Ρ€Π΅Π΄Π½Π΅Π΅ исполнСниС
' + - '
' + fullyExecuted + '
ΠŸΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ исполнСно
'; + '
' + bkvCount + '
Π‘ΠšΠ’
' + + '
' + obCount + '
ΠžΠ‘
'; } function renderTable() { @@ -205,11 +211,12 @@ const statusClass = getStatusClass(r.execPercent || 0); const statusText = getStatusText(r.execPercent || 0); return '' + - '' + '' + '' + + '' + '' + '' + + '' + '' + '' + ''; @@ -297,13 +304,14 @@ } function exportToExcel() { - const head = ["Код ΡΡ‚Π°Ρ‚ΡŒΠΈ", "НаимСнованиС услуги", "ΠŸΠΎΠ΄Ρ€Π°Π·Π΄Π΅Π»Π΅Π½ΠΈΠ΅", "Π‘ΡŽΠ΄ΠΆΠ΅Ρ‚", "ИсполнСно", "ИсполнСниС %", "Бтатус"]; + const head = ["НаимСнованиС", "ΠŸΠΎΠ΄Ρ€Π°Π·Π΄Π΅Π»Π΅Π½ΠΈΠ΅", "Π’ΠΈΠΏ Π±ΡŽΠ΄ΠΆΠ΅Ρ‚Π°", "Π‘ΡŽΠ΄ΠΆΠ΅Ρ‚ Π·Π°ΠΊΡƒΠΏΠΎΠΊ, Ρ‚Π³", "Π‘ΡƒΠΌΠΌΠ° поставки, Ρ‚Π³", "Экономия, Ρ‚Π³", "ИсполнСниС %", "Бтатус"]; const body = filteredData.map(r => [ - r.articleCode || '', r.serviceName || '', r.department || '', + r.budgetType || '', r.budgetSum || 0, r.executedSum || 0, + r.economy || 0, (r.execPercent || 0).toFixed(2), getStatusText(r.execPercent || 0) ]); diff --git a/server.js b/server.js index 7234550..11f82f5 100644 --- a/server.js +++ b/server.js @@ -25,18 +25,19 @@ function parseExcelData() { const findColumn = (names) => { for (const name of names) { - const idx = headers.findIndex(h => h.includes(name.toLowerCase())); + const idx = headers.findIndex(h => h.toLowerCase().includes(name.toLowerCase())); if (idx !== -1) return idx; } return -1; }; - const articleCodeIdx = findColumn(['ΠΊΠΎΠ΄ ΡΡ‚Π°Ρ‚ΡŒΠΈ', 'ΡΡ‚Π°Ρ‚ΡŒΡ', 'ΠΊΠΎΠ΄']); - const serviceNameIdx = findColumn(['Π½Π°ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Π½ΠΈΠ΅', 'услуг', 'Ρ€Π°Π±ΠΎΡ‚', 'Π½Π°Π·Π²Π°Π½ΠΈΠ΅']); - const departmentIdx = findColumn(['ΠΏΠΎΠ΄Ρ€Π°Π·Π΄Π΅Π»', 'Ρ†Π΅Π½Ρ‚Ρ€', 'Π΄Π΅ΠΏΠ°Ρ€Ρ‚Π°ΠΌΠ΅Π½Ρ‚', 'ΡƒΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅']); - const budgetSumIdx = findColumn(['Π±ΡŽΠ΄ΠΆΠ΅Ρ‚', 'ΠΏΠ»Π°Π½', 'сумма ΠΏΠ»Π°Π½']); - const executedSumIdx = findColumn(['исполн', 'Ρ„Π°ΠΊΡ‚', 'ΠΎΠΏΠ»Π°Ρ‡Π΅Π½ΠΎ']); - const execPercentIdx = findColumn(['исполнСниС', '%', 'ΠΏΡ€ΠΎΡ†Π΅Π½Ρ‚']); + const serviceNameIdx = findColumn(['ΠšΡ€Π°Ρ‚ΠΊΠΈΠΉ тСкст ΠΌΠ°Ρ‚Π΅Ρ€ΠΈΠ°Π»Π°']); + const budgetSumIdx = findColumn(['Π‘ΡŽΠ΄ΠΆΠ΅Ρ‚ Π·Π°ΠΊΡƒΠΏΠΎΠΊ,Ρ‚Π³']); + const executedSumIdx = findColumn(['Π‘ΡƒΠΌΠΌΠ° поставки,Ρ‚Π³']); + const economyIdx = findColumn(['Экономия ΠΎΡ‚ ΠΏΡ€ΠΎΠ²Π΅Π΄Π΅Π½Π½ΠΎΠ³ΠΎ Π·Π°ΠΊΡƒΠΏΠ°']); + const sppElementIdx = findColumn(['БПП-элСмСнт']); + const departmentIdx = findColumn(['Π—Π°Π²ΠΎΠ΄', 'ΠœΠ’Π—', 'ВСкст ΠœΠ’Π—']); + const execPercentIdx = findColumn(['% исполнСния']); const data = []; for (let i = 1; i < rows.length; i++) { @@ -45,8 +46,17 @@ function parseExcelData() { const budgetSum = parseFloat(String(row[budgetSumIdx] || 0).replace(/\s/g, '')) || 0; const executedSum = parseFloat(String(row[executedSumIdx] || 0).replace(/\s/g, '')) || 0; + const economy = parseFloat(String(row[economyIdx] || 0).replace(/\s/g, '')) || 0; + const sppElement = String(row[sppElementIdx] || '').trim(); + + let budgetType = ''; + if (sppElement.startsWith('I')) { + budgetType = 'Π‘ΠšΠ’'; + } else if (sppElement.startsWith('O')) { + budgetType = 'ΠžΠ‘'; + } + let execPercent = 0; - if (execPercentIdx !== -1 && row[execPercentIdx]) { execPercent = parseFloat(String(row[execPercentIdx]).replace('%', '').replace(/\s/g, '')) || 0; } else if (budgetSum > 0) { @@ -54,15 +64,17 @@ function parseExcelData() { } const item = { - articleCode: String(row[articleCodeIdx] || '').trim(), serviceName: String(row[serviceNameIdx] || '').trim(), department: String(row[departmentIdx] || '').trim(), + budgetType: budgetType, + sppElement: sppElement, budgetSum: budgetSum, executedSum: executedSum, + economy: economy, execPercent: Math.round(execPercent * 100) / 100 }; - if (item.serviceName || item.articleCode) { + if (item.serviceName) { data.push(item); } }
Код ΡΡ‚Π°Ρ‚ΡŒΠΈ ↕НаимСнованиС услуги ↕НаимСнованиС ↕ ΠŸΠΎΠ΄Ρ€Π°Π·Π΄Π΅Π»Π΅Π½ΠΈΠ΅ β†•Π‘ΡŽΠ΄ΠΆΠ΅Ρ‚ β†•Π˜ΡΠΏΠΎΠ»Π½Π΅Π½ΠΎ ↕Вип Π±ΡŽΠ΄ΠΆΠ΅Ρ‚Π° β†•Π‘ΡŽΠ΄ΠΆΠ΅Ρ‚ Π·Π°ΠΊΡƒΠΏΠΎΠΊ, Ρ‚Π³ ↕Бумма поставки, Ρ‚Π³ ↕Экономия, Ρ‚Π³ ↕ ИсполнСниС % ↕ Бтатус ↕
' + (r.articleCode || '-') + '' + (r.serviceName || '-') + '' + (r.department || '-') + '' + (r.budgetType || '-') + '' + formatAmount(r.budgetSum || 0) + '' + formatAmount(r.executedSum || 0) + '' + formatAmount(r.economy || 0) + '' + formatAmount(r.execPercent || 0) + '%' + statusText + '