- | ΠΠΎΠ΄ ΡΡΠ°ΡΡΠΈ β |
- ΠΠ°ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Π½ΠΈΠ΅ ΡΡΠ»ΡΠ³ΠΈ β |
+ ΠΠ°ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Π½ΠΈΠ΅ β |
ΠΠΎΠ΄ΡΠ°Π·Π΄Π΅Π»Π΅Π½ΠΈΠ΅ β |
- ΠΡΠ΄ΠΆΠ΅Ρ β |
- ΠΡΠΏΠΎΠ»Π½Π΅Π½ΠΎ β |
+ Π’ΠΈΠΏ Π±ΡΠ΄ΠΆΠ΅ΡΠ° β |
+ ΠΡΠ΄ΠΆΠ΅Ρ Π·Π°ΠΊΡΠΏΠΎΠΊ, ΡΠ³ β |
+ Π‘ΡΠΌΠΌΠ° ΠΏΠΎΡΡΠ°Π²ΠΊΠΈ, ΡΠ³ β |
+ ΠΠΊΠΎΠ½ΠΎΠΌΠΈΡ, ΡΠ³ β |
ΠΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ % β |
Π‘ΡΠ°ΡΡΡ β |
@@ -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 + '
ΠΠΎΠ»Π½ΠΎΡΡΡΡ ΠΈΡΠΏΠΎΠ»Π½Π΅Π½ΠΎ
';
+ '' +
+ '';
}
function renderTable() {
@@ -205,11 +211,12 @@
const statusClass = getStatusClass(r.execPercent || 0);
const statusText = getStatusText(r.execPercent || 0);
return '' +
- '| ' + (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 + ' | ' +
'
';
@@ -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);
}
}