198 lines
7.7 KiB
JavaScript
198 lines
7.7 KiB
JavaScript
const XLSX = require("xlsx");
|
||
const fs = require("fs");
|
||
|
||
// Чтение файлов
|
||
const wb1 = XLSX.readFile("БП_2026_НСВМ.xlsx");
|
||
const wb2 = XLSX.readFile("Шаблон БП_ БКВ 2027 с займами.xlsx");
|
||
|
||
// Получение списка всех листов
|
||
const sheets1 = wb1.SheetNames;
|
||
const sheets2 = wb2.SheetNames;
|
||
|
||
console.log("Файл 1 листы:", sheets1);
|
||
console.log("Файл 2 листы:", sheets2);
|
||
|
||
// Сравнительные данные
|
||
const comparisonResults = [];
|
||
|
||
// Сравниваем листы
|
||
const allSheets = [...new Set([...sheets1, ...sheets2])];
|
||
|
||
allSheets.forEach(sheetName => {
|
||
const sheet1Exists = sheets1.includes(sheetName);
|
||
const sheet2Exists = sheets2.includes(sheetName);
|
||
|
||
if (!sheet1Exists) {
|
||
comparisonResults.push({
|
||
"Лист": sheetName,
|
||
"Статус": "Отсутствует в файле 1 (БП_2026_НСВМ)",
|
||
"Различия": "Лист есть только во втором файле"
|
||
});
|
||
return;
|
||
}
|
||
|
||
if (!sheet2Exists) {
|
||
comparisonResults.push({
|
||
"Лист": sheetName,
|
||
"Статус": "Отсутствует в файле 2 (Шаблон БП_ БКВ 2027 с займами)",
|
||
"Различия": "Лист есть только в первом файле"
|
||
});
|
||
return;
|
||
}
|
||
|
||
// Получаем данные листов
|
||
const data1 = XLSX.utils.sheet_to_json(wb1.Sheets[sheetName], { header: 1 });
|
||
const data2 = XLSX.utils.sheet_to_json(wb2.Sheets[sheetName], { header: 1 });
|
||
|
||
// Сравниваем размеры
|
||
const rows1 = data1.length;
|
||
const rows2 = data2.length;
|
||
const cols1 = data1.length > 0 ? Math.max(...data1.map(r => r.length)) : 0;
|
||
const cols2 = data2.length > 0 ? Math.max(...data2.map(r => r.length)) : 0;
|
||
|
||
if (rows1 !== rows2 || cols1 !== cols2) {
|
||
comparisonResults.push({
|
||
"Лист": sheetName,
|
||
"Статус": "Разные размеры",
|
||
"Различия": `Файл 1: ${rows1} строк x ${cols1} колонок | Файл 2: ${rows2} строк x ${cols2} колонок`
|
||
});
|
||
}
|
||
|
||
// Построчное сравнение (первые 100 строк для производительности)
|
||
const maxRows = Math.min(Math.max(rows1, rows2), 100);
|
||
let diffCount = 0;
|
||
const diffDetails = [];
|
||
|
||
for (let i = 0; i < maxRows; i++) {
|
||
const row1 = data1[i] || [];
|
||
const row2 = data2[i] || [];
|
||
const maxCols = Math.max(row1.length, row2.length);
|
||
|
||
for (let j = 0; j < maxCols; j++) {
|
||
const val1 = row1[j] !== undefined ? row1[j] : null;
|
||
const val2 = row2[j] !== undefined ? row2[j] : null;
|
||
|
||
if (val1 !== val2 && val1 !== null && val2 !== null) {
|
||
diffCount++;
|
||
if (diffDetails.length < 20) { // Сохраняем первые 20 различий
|
||
const colLabel = String.fromCharCode(65 + j); // A, B, C...
|
||
const rowNum = i + 1;
|
||
diffDetails.push(`Ячейка ${colLabel}${rowNum}: "${val1}" ≠ "${val2}"`);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (diffCount > 0) {
|
||
comparisonResults.push({
|
||
"Лист": sheetName,
|
||
"Статус": "Есть различия",
|
||
"Различия": `Найдено различий: ${diffCount}. Примеры: ${diffDetails.slice(0, 5).join("; ")}`
|
||
});
|
||
} else {
|
||
comparisonResults.push({
|
||
"Лист": sheetName,
|
||
"Статус": "Идентичен",
|
||
"Различия": "Нет"
|
||
});
|
||
}
|
||
});
|
||
|
||
// Создаём итоговый файл с результатами сравнения
|
||
const resultWorkbook = XLSX.utils.book_new();
|
||
|
||
// Лист 1: Сводная таблица сравнения
|
||
const summaryData = [
|
||
["СРАВНЕНИЕ ФАЙЛОВ"],
|
||
["Файл 1:", "БП_2026_НСВМ.xlsx"],
|
||
["Файл 2:", "Шаблон БП_ БКВ 2027 с займами.xlsx"],
|
||
["Дата сравнения:", new Date().toLocaleString("ru-RU")],
|
||
[],
|
||
["СВОДНАЯ ТАБЛИЦА РАЗЛИЧИЙ"],
|
||
["Лист", "Статус", "Различия"]
|
||
];
|
||
|
||
comparisonResults.forEach(r => {
|
||
summaryData.push([r["Лист"], r["Статус"], r["Различия"]]);
|
||
});
|
||
|
||
const summarySheet = XLSX.utils.json_to_sheet(summaryData.slice(7), { skipHeader: false });
|
||
XLSX.utils.sheet_add_aoa(summarySheet, summaryData.slice(0, 7), { origin: "A1" });
|
||
XLSX.utils.book_append_sheet(resultWorkbook, summarySheet, "Сводка");
|
||
|
||
// Лист 2: Детальное сравнение по ячейкам
|
||
const detailData = [["Лист", "Ячейка", "Значение в файле 1", "Значение в файле 2", "Тип различия"]];
|
||
|
||
sheets1.forEach(sheetName => {
|
||
if (!sheets2.includes(sheetName)) return;
|
||
|
||
const data1 = XLSX.utils.sheet_to_json(wb1.Sheets[sheetName], { header: 1 });
|
||
const data2 = XLSX.utils.sheet_to_json(wb2.Sheets[sheetName], { header: 1 });
|
||
const maxRows = Math.min(Math.max(data1.length, data2.length), 200);
|
||
|
||
for (let i = 0; i < maxRows; i++) {
|
||
const row1 = data1[i] || [];
|
||
const row2 = data2[i] || [];
|
||
const maxCols = Math.max(row1.length, row2.length);
|
||
|
||
for (let j = 0; j < maxCols; j++) {
|
||
const val1 = row1[j] !== undefined ? row1[j] : null;
|
||
const val2 = row2[j] !== undefined ? row2[j] : null;
|
||
|
||
if (val1 !== val2 && val1 !== null && val2 !== null) {
|
||
const colLabel = String.fromCharCode(65 + j);
|
||
const rowNum = i + 1;
|
||
detailData.push([
|
||
sheetName,
|
||
`${colLabel}${rowNum}`,
|
||
String(val1),
|
||
String(val2),
|
||
typeof val1 !== typeof val2 ? "Разный тип данных" : "Разное значение"
|
||
]);
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
const detailSheet = XLSX.utils.json_to_sheet(detailData.slice(1), { skipHeader: false });
|
||
XLSX.utils.sheet_add_aoa(detailSheet, [detailData[0]], { origin: "A1" });
|
||
XLSX.utils.book_append_sheet(resultWorkbook, detailSheet, "Детали различий");
|
||
|
||
// Лист 3: Структура файлов
|
||
const structureData = [
|
||
["СТРУКТУРА ФАЙЛОВ"],
|
||
[],
|
||
["Файл 1: БП_2026_НСВМ.xlsx"],
|
||
["№", "Название листа", "Строк", "Колонок"]
|
||
];
|
||
|
||
sheets1.forEach((name, idx) => {
|
||
const sheet = XLSX.utils.sheet_to_json(wb1.Sheets[name], { header: 1 });
|
||
const rows = sheet.length;
|
||
const cols = rows > 0 ? Math.max(...sheet.map(r => r.length)) : 0;
|
||
structureData.push([idx + 1, name, rows, cols]);
|
||
});
|
||
|
||
structureData.push([], [], ["Файл 2: Шаблон БП_ БКВ 2027 с займами.xlsx"], ["№", "Название листа", "Строк", "Колонок"]);
|
||
|
||
sheets2.forEach((name, idx) => {
|
||
const sheet = XLSX.utils.sheet_to_json(wb2.Sheets[name], { header: 1 });
|
||
const rows = sheet.length;
|
||
const cols = rows > 0 ? Math.max(...sheet.map(r => r.length)) : 0;
|
||
structureData.push([idx + 1, name, rows, cols]);
|
||
});
|
||
|
||
const structureSheet = XLSX.utils.json_to_sheet(structureData.slice(4), { skipHeader: false });
|
||
XLSX.utils.sheet_add_aoa(structureSheet, structureData.slice(0, 4), { origin: "A1" });
|
||
XLSX.utils.book_append_sheet(resultWorkbook, structureSheet, "Структура файлов");
|
||
|
||
// Сохраняем результат
|
||
const outputFile = "Сравнение_БП_2026_НСВМ_и_Шаблон_БП_БКВ_2027.xlsx";
|
||
XLSX.writeFile(resultWorkbook, outputFile);
|
||
|
||
console.log(`\n✅ Сравнение завершено!`);
|
||
console.log(`📊 Файл с результатами: ${outputFile}`);
|
||
console.log(`📈 Всего листов сравнено: ${allSheets.length}`);
|
||
console.log(`⚠️ Листов с различиями: ${comparisonResults.filter(r => r["Статус"] === "Есть различия").length}`);
|
||
console.log(`📋 Листов без различий: ${comparisonResults.filter(r => r["Статус"] === "Идентичен").length}`);
|