/* Сводный финансовый анализ дочерних компаний — статичный макет, состояние в localStorage */ (function () { "use strict"; var LS = "sfa-do-v1"; function loadState() { var base = { screen: "upload", fileStatus: {}, fileManual: false, mappingMode: "auto", unitFixed: [], deviations: [], aiAccepted: false, sent: false, analysis: {}, done: { recognize: true, check: false, analysis: false, result: false }, theme: "light" }; try { var raw = localStorage.getItem(LS); if (raw) { var s = JSON.parse(raw); for (var k in base) if (s && k in s) base[k] = s[k]; } } catch (e) { if (!localStorage.getItem(LS)) base.resetFlag = true; } return base; } function save() { try { localStorage.setItem(LS, JSON.stringify(S)); } catch (e) {} } var S = loadState(); var COMPANIES = [ ["ООО А", 12500, 11800, 3100, 3050, 420, 510, 640, 590, 1840, 2110, 1240, 1215], ["ООО Б", 8200, 7950, 2100, 1980, 280, 310, 410, 390, 1220, 1150, 860, 842], ["ООО В", 15400, 13100, 4100, 3480, 530, 470, 880, 720, 2450, 2610, 1580, 1595], ["ООО Г", 9800, 10050, 2500, 2560, 340, 355, 520, 480, 1480, 1390, 1010, 998], ["ООО Д", 11300, 10900, 2850, 2700, 390, 430, 610, 585, 1620, 1705, 1130, 1128], ["ООО Е", 7600, 7420, 1900, 1830, 250, 270, 380, 350, 1090, 1160, 780, 775], ["ООО Ж", 13900, 14350, 3600, 3720, 480, 455, 760, 730, 2180, 2040, 1420, 1408], ["ООО З", 6700, 6510, 1680, 1610, 220, 240, 330, 300, 960, 1015, 690, 682], ["ООО И", 10400, 9980, 2640, 2490, 360, 385, 570, 545, 1490, 1565, 1040, 1035], ["ООО К", 8900, 8730, 2280, 2210, 300, 325, 450, 420, 1270, 1335, 915, 908], ["ООО Л", 12100, 12490, 3200, 3310, 430, 415, 690, 660, 1740, 1650, 1265, 1258], ["ООО М", 7100, 6890, 1780, 1720, 260, 280, 400, 365, 1030, 1085, 745, 740], ["ООО Н", 14600, 13850, 3850, 3640, 505, 525, 830, 790, 2320, 2415, 1495, 1488], ["ООО О", 9300, 8910, 2380, 2240, 330, 350, 510, 480, 1360, 1425, 965, 958], ["ООО П", 11700, 11980, 3000, 3080, 405, 395, 640, 615, 1580, 1495, 1175, 1168], ["ООО Р", 8400, 8160, 2150, 2060, 290, 310, 430, 400, 1180, 1240, 835, 830], ["ООО С", 13200, 12840, 3400, 3280, 460, 485, 710, 680, 1950, 2020, 1355, 1348] ]; var CO = COMPANIES.map(function (r) { return { name: r[0], vp: r[1], vf: r[2], ep: r[3], ef: r[4], cap: r[5], caf: r[6], dp: r[7], df: r[8], cp: r[9], cf: r[10], hp: r[11], hf: r[12] }; }); function dev(o, f, v) { return v > 0 ? Math.round(Math.abs(f - v) / v * 1000) / 10 : 0; } CO.forEach(function (c) { c.rDev = dev(c, c.vf, c.vp); c.eDev = dev(c, c.ef, c.ep); c.cDev = dev(c, c.caf, c.cap); c.dDev = dev(c, c.df, c.dp); c.kDev = dev(c, c.cf, c.cp); c.maxDev = Math.max(c.rDev, c.eDev, c.cDev, c.dDev, c.kDev); }); var FILES = [ ["Отчет_ОАОА_июнь.xlsx", 0], ["Fin_report_ООО-Б_0626.xlsx", 1], ["Свод_В_план_факт.xlsx", 2], ["Отчет_Г_июнь_2026.xlsx", 3], ["Fin_Д_06-2026.xlsx", 4], ["Финансы_Е_июнь.xlsx", 5], ["Отчет_Ж.xlsx", 6], ["Годовой_отчет_З.xlsx", 7], ["Данные_И_июнь.xlsx", 8], ["Report_K_2026-06.xlsx", 9], ["Свод_Л_июнь.xlsx", 10], ["Fin_М_06.xlsx", 11], ["Отчет_Н_июнь_2026.xlsx", 12], ["Data_O_2026.06.xlsx", 13], ["Отчет_П.xlsx", 14], ["Фин_Р_июнь.xlsx", 15], ["Summary_S_06.2026.xlsx", 16] ]; var MAPPING = [ ["Выручка", { 0: "Раздел_1_Оборот", 1: "Выручка_в_тыс", 2: "Выручка", 3: "ОБорот_за_отчетный_период", 4: "Revenue", 5: "ОП_Выручка", 6: "Выручка(нетто)", 7: "Выручка год", 8: "ОП-1 Выручка", 9: "REV_06", 10: "Выручка", 11: "Rev", 12: "Выручка", 13: "Sales", 14: "О_п_факт", 15: "Выручка_итоги", 16: "Revenue_06" }], ["EBITDA", { 0: "EBITDA", 1: "Прибыль_до_аморт", 2: "EBITDA_план", 3: "EBITDA факт", 4: "EBITDA", 5: "EBITDA_итого", 6: "Прибыль_EBITDA", 7: "EBITDA_год", 8: "EBITDA факт", 9: "EBITDA_06", 10: "EBITDA", 11: "eBITDA", 12: "EBITDA факт", 13: "EBITDA", 14: "EBITDA_факт", 15: "EBITDA", 16: "EBITDA_06" }], ["CAPEX", { 0: "Инвест_затраты", 1: "Капзатраты_тыс", 2: "CAPEX", 3: "Инвестпроект_затраты", 4: "Capex", 5: "CAPEX_итоги", 6: "Инвест.затраты", 7: "CAPEX_год", 8: "CAPEX факт", 9: "CAPEX_06", 10: "CAPEX", 11: "capex", 12: "CAPEX факт", 13: "CAPEX", 14: "ИЗ_факт", 15: "Капзатраты", 16: "Capex_06" }], ["Дебиторка", { 0: "Дебиторка_итог", 1: "ДК_в_сальдо", 2: "Дебиторская", 3: "Д-ка_на_конец_мес", 4: "AR", 5: "Дебиторка_кон", 6: "ДК(кон.ост.)", 7: "Дебиторка_год", 8: "ДК факт", 9: "AR_06", 10: "Дебиторская", 11: "AR", 12: "ДК к/мес", 13: "AR", 14: "ДК_факт", 15: "Дебиторка_итого", 16: "AR_06" }], ["Кредиторка", { 0: "Кредиторка_итог", 1: "КК_в_сальдо", 2: "Кредиторская", 3: "К-ка_на_конец_мес", 4: "AP", 5: "Кредиторка_кон", 6: "КК(кон.ост.)", 7: "Кредиторка_год", 8: "КК факт", 9: "AP_06", 10: "Кредиторская", 11: "AP", 12: "КК к/мес", 13: "AP", 14: "КК_факт", 15: "Кредиторка_итого", 16: "AP_06" }], ["Численность", { 0: "Штат_ФТ", 1: "Числ._по_окладу", 2: "Численность", 3: "Штатное_число", 4: "Heads", 5: "Численность_кон", 6: "ФТ_кон.оот.", 7: "Численность_год", 8: "Штат факт", 9: "HC_06", 10: "Численность", 11: "FTE", 12: "Штат к/мес", 13: "HC", 14: "Числ_факт", 15: "Численность_итого", 16: "Headcount_06" }] ]; var LOGS = [ ["10:02:14", "ok", "Получено 17 вложений из общего ящика Outlook"], ["10:02:15", "info", "Форматы: 17 Excel, 3 нестандартных шаблона"], ["10:02:18", "ok", "ООО А · сопоставлено 6 столбцов (шаблон 2024)"], ["10:02:21", "warn", "ООО Б · «Выручка_в_тыс» → «Выручка», множитель 1/1000"], ["10:02:23", "ok", "ООО В..И · автоматическое сопоставление (6/6)"], ["10:02:27", "warn", "ООО О · пустые строки в файле, пропущены"], ["10:02:29", "ok", "ООО Л..С · автоматическое сопоставление (6/6)"], ["10:02:31", "info", "1 расхождение единиц измерения, отклонения свыше 5% — 3 компании"] ]; var ANALYSIS = { 0: "Выручка 11 800 млн тг — 94% плана (−6%). EBITDA фактически выше плана (101%), операционная эффективность сохранена: CAPEX 510 млн тг при плане 420. Дебиторка +8%, кредиторка +3% — оборотный капитал в пределах нормы. Численность 1215 чел. при плане 1240.", 1: "Выручка 7 950 млн тг — 97% плана. Обнаружено расхождение единиц: в файле выручка в тысячах тенге, при переносе применён множитель 1/1000. EBITDA 94% плана, отклонение в пределах допустимого. Численность 842 чел.", 2: "Выручка 13 100 млн тг — −15% к плану, крупнейшее отклонение в группе. EBITDA 3 480 млн тг при плане 4 100 (−15%), доля в выручке снизилась с 27% до 27%. Дебиторка 1 720 при 880 — рост 96% требует разбора. Кредиторка 2 610 (96% плана).", 3: "Выручка 10 050 млн тг — 103% плана, опережение. EBITDA 102% плана. CAPEX 355 (104%). Дебиторка 480 (92% плана). Компенсация плановых отставок, численность 998 чел. при плане 1 010.", 4: "Выручка 10 900 млн тг — 96% плана. EBITDA 2 700 при 2 850 (95%). CAPEX 430 (110% плана) — опережение инвестиционной программы. Численность 1 128 чел.", 5: "Выручка 7 420 млн тг — 98% плана. EBITDA 1 830 (96%). Отклонения в пределах 5% по всем статьям. Численность 775 чел.", 6: "Выручка 14 350 млн тг — 103% плана, опережение. EBITDA 3 720 (103%). Дебиторка 730 (96% плана). Рост органический, численность 1 408 чел.", 7: "Выручка 6 510 млн тг — 97% плана. EBITDA 1 610 (96%). Отклонений свыше 5% нет. Численность 682 чел.", 8: "Выручка 9 980 млн тг — 96% плана, снижение 4% к плану. EBITDA 2 490 (94%). Кредиторка 1 565 (106% плана). Численность 1 035 чел.", 9: "Выручка 8 730 млн тг — 98% плана. EBITDA 2 210 (97%). Отклонения минимальные. Численность 908 чел.", 10: "Выручка 12 490 млн тг — 103% плана, опережение. EBITDA 3 310 (103%). CAPEX 415 (97%). Численность 1 258 чел.", 11: "Выручка 6 890 млн тг — 97% плана. EBITDA 1 720 (97%). Отклонений свыше 5% нет. Численность 740 чел.", 12: "Выручка 13 850 млн тг — 95% плана. EBITDA 3 640 (95%). Дебиторка 790 (95% плана), кредиторка 2 415 (104%). Численность 1 488 чел.", 13: "Выручка 8 910 млн тг — 96% плана. EBITDA 2 240 (94%). Дебиторка 480 (94%). Численность 958 чел.", 14: "Выручка 11 980 млн тг — 103% плана, опережение. EBITDA 3 080 (103%). Кредиторка 1 495 (95% плана). Численность 1 168 чел.", 15: "Выручка 8 160 млн тг — 97% плана. EBITDA 2 060 (96%). Отклонения в пределах 5%. Численность 830 чел.", 16: "Выручка 12 840 млн тг — 97% плана. EBITDA 3 280 (96%). Дебиторка 680 (96%). Численность 1 348 чел." }; CO.forEach(function (c, i) { c.analysis = ANALYSIS[i]; c.aStatus = 0; }); CO[2].aStatus = 2; var A_ERR = CO[2]; var STEPS = [ { id: "upload", n: 1, t: "Загрузка", sub: "распознавание файлов" }, { id: "check", n: 2, t: "Проверка", sub: "данные и отклонения" }, { id: "analysis", n: 3, t: "Черновик", sub: "анализа" }, { id: "result", n: 4, t: "Итог", sub: "результат и отправка" } ]; /* ---------- utils ---------- */ var T = "light"; function $(id) { return document.getElementById(id); } function esc(s) { return String(s).replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); } var NLC = new Intl.NumberFormat("ru-RU"); function fmt(n) { return n == null ? "—" : NLC.format(n); } function pct(d) { if (!d) return "0"; return "−" + String(d).replace(".", ","); } function pctSign(d) { return (d > 0 ? "+" : "−") + String(d).replace(".", ","); } function ic(name) { return ''; } function toast(msg, tone) { var d = document.createElement("div"); d.className = "toast"; if (tone) d.setAttribute("data-tone", tone); d.textContent = msg; $("toasts").appendChild(d); setTimeout(function () { d.style.transition = "opacity .3s"; d.style.opacity = "0"; setTimeout(function () { d.remove(); }, 320); }, 3600); return true; } function sum(arr) { return arr.reduce(function (a, b) { return a + b; }, 0); } function fileStatus(i) { if (S.fileManual) return ["info", "Вручную"]; if (S.aiAccepted) return ["ok", "Сопоставлено"]; return S.fileStatus[i] || (i === 1 ? ["warn", "Единицы: ×1/1000"] : ["ok", "Сопоставлено"]); } /* ---------- shell ---------- */ function renderSidebar() { var items = STEPS.map(function (st) { var done = !!S.done[st.id]; var cur = S.screen === st.id; return ''; }); items.push(''); $("sbNav").innerHTML = '
' + items.join("") + '
'; } function renderTopbar() { var t = ""; if (S.screen === "upload") t = "Загрузка и распознавание · " + "пятница, 10:02"; else if (S.screen === "check") t = "Проверка данных и отклонений"; else if (S.screen === "analysis") t = "Черновик анализа"; else if (S.screen === "result") t = "Итоговый результат"; else t = "О решении"; $("topTitle").innerHTML = '' + t + 'пакет на пн, 15:00'; } function showScreen(id) { if (id !== "about" && !canOpen(id)) { toast("Сначала завершите предыдущий шаг", "warn"); return; } S.screen = id; save(); renderSidebar(); renderTopbar(); var r = { upload: viewUpload, check: viewCheck, analysis: viewAnalysis, result: viewResult, about: viewAbout }[id]; var el = $("view"); el.innerHTML = r(); el.scrollTop = 0; document.querySelector(".kt-ai-main").scrollTop = 0; bind(id); closeMobile(); } function canOpen(id) { if (id === "upload") return true; if (id === "check") return !!S.done.recognize; if (id === "analysis") return !!S.done.check; if (id === "result") return !!S.done.analysis || S.screen === "result"; return true; } function closeMobile() { var sb = $("sidebar"); if (sb) sb.removeAttribute("data-open"); $("scrim").classList.remove("on"); } /* ---------- stepper ---------- */ function stepHtml(curId) { var h = ""; STEPS.forEach(function (st, i) { var done = !!S.done[st.id]; var cur = curId === st.id; var cls = []; if (cur) cls.push('data-cur="true"'); if (done) cls.push('data-done="true"'); if (i > 0) h += ''; h += '' + (done ? ic("check") : st.n) + '' + st.t + ''; }); return '
' + h + '
'; } /* ================= ЭКРАН 1 — загрузка ================= */ function viewUpload() { var fileRows = FILES.map(function (f, i) { var st = fileStatus(i); return '
' + '' + ic("spreadsheet") + '' + '
' + esc(f[0]) + '
' + '
' + esc(CO[i].name) + ' · получен 10 июн, ' + (9 + i % 4) + ':' + String((5 + i * 7) % 60).padStart(2, "0") + '
' + '' + st[1] + '
'; }).join(""); var mapRows = MAPPING.map(function (m) { var orig = Object.keys(m[1]).map(function (k) { return m[1][k]; }); return '' + m[0] + '' + '' + orig.map(function (o) { return esc(o); }).join(" · ") + "" + '' + m[0] + ''; }).join(""); var progress = S.fileManual ? 100 : S.aiAccepted ? 100 : 82; var banner, tone; if (S.fileManual) { tone = "warn"; banner = "Распознавание остановлено: 1 файл обработан в ручном режиме. Проверьте столбцы и верните автоматический режим."; } else if (S.aiAccepted) { tone = "ok"; banner = "Все 17 файлов сопоставлены, расхождения единиц отмечены. Можно переходить к проверке данных."; } else { tone = "info"; banner = "ИИ сопоставляет столбцы по 17 файла. Отклонения и единицы будут отмечены после завершения."; } banner = '
' + banner + '
'; return pageHead("Загрузка и распознавание", "Вложения из общего ящика Outlook · 17 дочерних компаний, отчёт за июнь", '', '' + '') + banner + stepHtml("upload") + '
' + '

Файлы

17 шт. · июнь 2026' + (S.aiAccepted ? '' : "") + '
' + '
' + fileRows + '
' + '

Прогресс распознавания

' + '
' + progress + '%' + (S.aiAccepted ? "17/17 файлов" : "14/17 файлов") + 'автоматически
' + '
' + '
Сопоставление: 6 целевых показателей во всех файлах
' + '

Журнал действий ИИ

' + '
' + LOGS.map(function (l) { return '
' + l[0] + '' + '' + (l[1] === "ok" ? "готово" : l[1] === "warn" ? "внимание" : "инфо") + '' + '

' + l[2] + '

'; }).join("") + '
' + '
' + '

Сопоставление столбцов

исходный формат → единый шаблон
' + '
' + mapRows + '
ПоказательИсходный столбец (варианты)Целевой столбец
'; } /* ================= ЭКРАН 2 — проверка ================= */ var chk = { company: "all", only: "all" }; function viewCheck() { var opts = "" + CO.map(function (c, i) { return ""; }).join(""); var devs = CO.filter(function (c) { return c.maxDev > 5; }).length; var kpis = kpi("Компаний в сводном файле", "17 / 17", "план и факт, июнь") + kpi("Отклонения плана > 5%", devs, "из 17 компаний") + kpi("Единицы измерения", S.unitFixed.length ? "исправлено" : "1 / 17", "ООО Б · ×1/1000") + kpi("Структура", "6 показателей", "итоговый лист + 17 вкладок"); return pageHead("Проверка данных и отклонения", "Сводный файл сформирован ИИ · проверьте единицы измерения и отклонения от плана", '', '") + stepHtml("check") + '
' + kpis + '
' + '

Сводная таблица

' + '
' + '
' + '
' + '' + '
' + '
' + '

Отклонения подсвечены: двухсегментная шкала — 2% и 5%. Строка с ошибками единиц помечена красным; исправление — по клику на ячейку. Итоговая строка — сумма по выбранным компаниям.

' + '
' + unitBanner(); } function kpi(label, value, hint) { return '
' + label + '' + value + '' + hint + '
'; } function unitBanner() { var fixed = S.unitFixed.indexOf(1) !== -1; return fixed ? '
Единицы ООО Б исправлены: выручка переведена из тысяч в миллионы тенге.
' : '
Расхождение единиц: ООО Б. В файле выручка в тысячах тенге, в шаблоне — в миллионах. Клик по ячейке выручки — исправить вручную.
'; } function totalRow() { var r = ["Всего", sum(CO.map(function (c) { return c.vp; })), sum(CO.map(function (c) { return c.vf; })), sum(CO.map(function (c) { return c.ep; })), sum(CO.map(function (c) { return c.ef; })), sum(CO.map(function (c) { return c.cap; })), sum(CO.map(function (c) { return c.caf; })), sum(CO.map(function (c) { return c.dp; })), sum(CO.map(function (c) { return c.df; })), sum(CO.map(function (c) { return c.cp; })), sum(CO.map(function (c) { return c.cf; })), sum(CO.map(function (c) { return c.hp; })), sum(CO.map(function (c) { return c.hf; }))]; return r; } function renderSumTable() { var box = $("sumTable"); if (!box) return; var filtered = CO.filter(function (c, i) { return chk.company === "all" || chk.company === String(i); }); var rows = CO.map(function (c, i) { var show = chk.company === "all" || chk.company === String(i); if (!show) return ""; if (chk.only === "dev" && c.maxDev <= 5) return ""; if (chk.only === "unit" && c.name !== "ООО Б") return ""; var unitFix = S.unitFixed.indexOf(1) !== -1; var dR = c.rDev, dE = c.eDev, dC = c.cDev, dD = c.dDev, dK = c.kDev; function cell(plan, fact, d, extra) { var cls = "num cell-num" + (d >= 5 ? " cell-dev-3" : d > 2 ? " cell-dev-2" : ""); return '' + fmt(plan) + ' / ' + fmt(fact) + ' (' + (d > 0 ? pct(d) : "0") + '%)'; } var vfCell = c.name === "ООО Б" ? '' + fmt(c.vp) + ' / ' + fmt(c.vf) + ' (' + pct(dR) + '%) ' + (unitFix ? 'исправлено' : '×1 000') + '' : cell(c.vp, c.vf, dR); return '' + esc(c.name) + '' + vfCell + cell(c.ep, c.ef, dE) + cell(c.cap, c.caf, dC) + cell(c.dp, c.df, dD) + cell(c.cp, c.cf, dK) + '' + fmt(c.hp) + ' / ' + fmt(c.hf) + ''; }).join(""); var totalN = filtered.length; var tsum = filtered.map(function (c) { return { vp: c.vp, vf: c.vf, ep: c.ep, ef: c.ef, cap: c.cap, caf: c.caf, dp: c.dp, df: c.df, cp: c.cp, cf: c.cf, hp: c.hp, hf: c.hf }; }).reduce(function (a, b) { for (var k in a) a[k] += b[k]; return a; }, { vp: 0, vf: 0, ep: 0, ef: 0, cap: 0, caf: 0, dp: 0, df: 0, cp: 0, cf: 0, hp: 0, hf: 0 }); var dV = dev({}, tsum.vf, tsum.vp), dE = dev({}, tsum.ef, tsum.ep), dC = dev({}, tsum.caf, tsum.cap), dD = dev({}, tsum.df, tsum.dp), dK = dev({}, tsum.cf, tsum.cp); function t(p, f, d) { return '' + fmt(p) + ' / ' + fmt(f) + ' (' + (d > 0 ? pct(d) : "0") + '%)'; } var totalRowHtml = 'Итого, ' + (chk.company === "all" ? "17 компаний" : esc(CO[+chk.company].name)) + '' + t(tsum.vp, tsum.vf, dV) + t(tsum.ep, tsum.ef, dE) + t(tsum.cap, tsum.caf, dC) + t(tsum.dp, tsum.df, dD) + t(tsum.cp, tsum.cf, dK) + '' + fmt(tsum.hp) + ' / ' + fmt(tsum.hf) + ''; var any = rows.indexOf("Выручка, млн тгEBITDA, млн тгCAPEX, млн тгДебиторка, млн тгКредиторка, млн тгЧисленность, чел." + (any ? rows + totalRowHtml : 'Под фильтр не попали компании') + ""; var note = $("fNote"); if (note) note.textContent = any ? totalN + " из 17 · план / факт, откл. в %" : ""; } function pageHead(title, sub, left, right) { return '

' + title + '

' + (sub || "") + '

' + (right ? '
' + right + '
' : "") + '
'; } /* ================= ЭКРАН 3 — черновик анализа ================= */ var curA = 0; function analysisText(i) { var o = S.analysis[i]; if (o && typeof o === "string") return o; return CO[i].analysis; } function aStatus(i) { return (S.analysis[i] && S.analysis[i].status) || CO[i].aStatus || 0; } function viewAnalysis() { var list = CO.map(function (c, i) { var st = aStatus(i); var dot = st === 1 ? 'dot-ok' : (st === 2 ? 'dot-err' : 'dot-gen'); return '
' + '' + ic("file") + '' + '
' + esc(c.name) + (S.analysis[i] && S.analysis[i].edited ? ' правка' : "") + '
' + '
'; }).join(""); var words = CO.reduce(function (a, c, i) { return a + analysisText(i).split(/\s+/).filter(Boolean).length; }, 0); var ready = CO.filter(function (c, i) { return aStatus(i) === 1; }).length; var memoKpi = kpi("Компаний", CO.length, "по одному выводу") + kpi("Принято", ready, "из " + CO.length) + kpi("Объём записки", fmt(words) + " слов", "≈ " + (words > 640 ? "2" : "1") + " стр.") + kpi("Готовность", ready === CO.length ? "100%" : Math.round(ready / CO.length * 100) + "%", "к формированию записки"); return pageHead("Черновик анализа", "ИИ сгенерировал выводы по каждой компании · отредактируйте при необходимости", '', '' + '') + stepHtml("analysis") + '
' + memoKpi + '
' + '
' + '
' + list + '
' + '
Черновик · ' + esc(CO[curA].name) + '' + words + ' слов' + '' + '
' + '
' + '
' + '' + 'Статус: ' + (aStatus(curA) === 1 ? "принято" : aStatus(curA) === 2 ? "есть пометка о данных" : "черновик") + '' + (aStatus(curA) === 2 ? 'проверить дебиторку' : "") + '
' + '
'; } /* ================= ЭКРАН 4 — итоговый результат ================= */ function viewResult() { var tv = totalRow(); var dV = dev({}, tv[2], tv[1]), dE = dev({}, tv[4], tv[3]); var memoRows = CO.filter(function (c) { return c.maxDev > 5 || c.name === "ООО Б"; }).map(function (c) { return "

• " + esc(c.name) + ". " + analysisTextShort(c) + "

"; }).join(""); var status = S.sent ? ['data-status="ok"', "Отправлено директору 09:58", ""] : S.done.analysis ? ['data-status="ok"', "Готово к отправке", "дедлайн: пн 15:00"] : ['data-status="warn"', "Ожидает подтверждения анализа", "дедлайн: пн 15:00"]; var xTable = '' + CO.map(function (c) { return ''; }).join("") + '
ОрганизацияВыручка, млн тгEBITDA, млн тгCAPEX, млн тгДебит. / Кредит., млн тг
' + esc(c.name) + '' + fmt(c.vf) + '' + fmt(c.ef) + '' + fmt(c.caf) + '' + fmt(c.df) + ' / ' + fmt(c.cf) + '
'; return pageHead("Итоговый результат", "Финальная проверка пакета перед передачей финансовому директору", '', '' + '' + '') + stepHtml("result") + '
' + '' + status[1] + ' · ' + status[2] + (S.sent ? ' · адресат: финансовый директор' : "") + '
' + '
' + kpi("Сводный файл", "1 итог + 17 листов", "сводный_файл_июнь.xlsx") + kpi("Записка", "2 страницы", "выводы по компаниям") + kpi("Сверка", "17 / 17", "единицы согласованы") + kpi("Отклонения", dV > 0 ? "−" + pctSign(dV) + "%" : pctSign(dV) + "%", "выручка к плану · EBITDA " + pctSign(dE) + "%") + '
' + '
' + '

Сводный Excel

sheet 1 из 18: Итог, затем по организации
' + '
' + xTable + '
' + '

Записка к отчёту

2 стр.
' + '
На: финансовый директор · От: аналитик · Пн, 15:00
' + '

Свод по дочерним компаниям, июнь

' + '

По всем 17 компаниям факт к плану по выручке ' + (dV > 0 ? "−" + pctSign(dV) : pctSign(dV)) + "%, по EBITDA — " + pctSign(dE) + "% (млн тг). Единицы измерения 1 компании (ООО Б) исправлены и пересчитаны.

" + memoRows + '

Рекомендации: разобрать дебиторку ООО В (рост 96% к плану) и CAPEX ООО Д (110% к программе).

' + '
' + '
'; } function analysisTextShort(c) { var t = c.analysis; return t.length > 200 ? t.slice(0, 197).replace(/\s+\S*$/, "") + "…" : t; } /* ================= действия ================= */ function setUnitFix(i, fixed) { var idx = S.unitFixed.indexOf(1); if (fixed && idx === -1) { S.unitFixed.push(1); } if (!fixed) S.unitFixed = S.unitFixed.filter(function (x) { return x !== 1; }); save(); if (S.screen === "check") renderSumTable(); updateUnitBanner(); toast(fixed ? "Единицы ООО Б исправлены: ×1/1000 применён" : "Единицы ООО Б возвращены в исходные", fixed ? "ok" : "warn"); } function updateUnitBanner() { if (S.screen !== "check") return; var banner = document.querySelector("#view .kt-ai-banner:not([data-tone='info']):not([data-tone='warn'])"); if (!banner) return; var fixed = S.unitFixed.indexOf(1) !== -1; banner.setAttribute("data-tone", fixed ? "ok" : "risk"); banner.innerHTML = fixed ? 'Единицы ООО Б исправлены: выручка переведена из тысяч в миллионы тенге.' : 'Расхождение единиц: ООО Б. В файле выручка в тысячах тенге, в шаблоне — в миллионах. Клик по ячейке выручки — исправить вручную.'; var k = document.querySelectorAll(".kt-ai-kpi"); if (k[2]) k[2].querySelector(".value").textContent = fixed ? "исправлено" : "1 / 17"; } function acceptAllAnalysis() { CO.forEach(function (c, i) { S.analysis[i] = { status: 1 }; }); S.done.analysis = true; save(); toast("Черновик принят по всем 17 компаниям", "ok"); showScreen("analysis"); } function makeMemo() { if (!S.done.analysis) { toast("Сначала примите черновик анализа по всем компаниям", "warn"); return; } showScreen("result"); toast("Записка сформирована", "ok"); } function download(text, filename) { try { var b = new Blob([text], { type: "text/plain;charset=utf-8" }); var a = document.createElement("a"); a.href = URL.createObjectURL(b); a.download = filename; document.body.appendChild(a); a.click(); a.remove(); setTimeout(function () { URL.revokeObjectURL(a.href); }, 2000); toast("Скачивание: " + filename, "ok"); } catch (e) { toast("Не удалось скачать файл", "warn"); } } function xlsxText() { return "Сводный файл, июнь 2026, млн тг (дебиторка/кредиторка/численность — свои единицы)\n\nКомпания;Выручка (план);Выручка (факт);EBITDA (план);EBITDA (факт);CAPEX (план);CAPEX (факт);Дебиторка (план);Дебиторка (факт);Кредиторка (план);Кредиторка (факт);Численность (план);Численность (факт)\n" + CO.map(function (c) { return [c.name, c.vp, c.vf, c.ep, c.ef, c.cap, c.caf, c.dp, c.df, c.cp, c.cf, c.hp, c.hf].join(";"); }).join("\n") + "\n\n" + CO.map(function (c) { return "== " + c.name + " ==\n" + analysisText(CO.indexOf(c)); }).join("\n\n"); } function memoText() { var tv = totalRow(); var dV = dev({}, tv[2], tv[1]), dE = dev({}, tv[4], tv[3]); return "ЗАПИСКА — свод по дочерним компаниям, июнь 2026\nНа: финансовый директор\n\nПо всем 17 компаниям факт к плану: выручка " + pctSign(dV) + "%, EBITDA " + pctSign(dE) + "% (млн тг).\nЕдиницы измерения ООО Б исправлены (тысячи → миллионы тенге).\n\n" + CO.filter(function (c) { return c.maxDev > 5 || c.name === "ООО Б"; }).map(function (c) { return "• " + c.name + ". " + analysisTextShort(c); }).join("\n") + "\n\nРекомендации: разобрать дебиторку ООО В (рост 96% к плану) и CAPEX ООО Д (110% к программе).\n"; } /* ---------- bind ---------- */ function bind(screen) { var v = $("view"); v.querySelectorAll("[data-goto]").forEach(function (b) { b.addEventListener("click", function () { showScreen(b.getAttribute("data-goto")); }); }); // загрузка if (screen === "upload") { v.querySelectorAll("[data-act='outlook']").forEach(function (b) { b.addEventListener("click", function () { toast("Обнаружено 17 вложений в общем ящике · загрузка завершена", "ok"); }); }); v.querySelectorAll("[data-act='recognize']").forEach(function (b) { b.addEventListener("click", function () { if (S.aiAccepted) { if (S.done.recognize) showScreen("check"); return; } S.aiAccepted = true; S.done.recognize = true; save(); toast("Сопоставление завершено: 17/17 файлов, 1 расхождение единиц", "ok"); showScreen("upload"); }); }); v.querySelectorAll("[data-act='manual']").forEach(function (b) { b.addEventListener("click", function () { S.fileManual = true; save(); toast("Открыт ручной режим для 1 файла. Проверьте столбцы и верните автоматический режим.", "warn"); showScreen("upload"); }); }); v.querySelectorAll("[data-file]").forEach(function (r) { r.addEventListener("click", function () { toast(esc(CO[+r.getAttribute("data-file")].name) + " · формат распознан, 6/6 столбцов", "ok"); }); }); } // проверка if (screen === "check") { renderSumTable(); var fc = $("fCompany"); if (fc) { fc.value = chk.company; fc.addEventListener("change", function () { chk.company = fc.value; renderSumTable(); }); } var fo = $("fOnly"); if (fo) fo.addEventListener("click", function (e) { var b = e.target.closest(".seg"); if (!b) return; chk.only = b.getAttribute("data-v"); fo.querySelectorAll(".seg").forEach(function (x) { x.setAttribute("data-active", x === b ? "true" : "false"); }); renderSumTable(); }); var box = $("sumTable"); if (box) box.addEventListener("click", function (e) { var cell = e.target.closest("[data-unit]"); if (cell) { setUnitFix(1, S.unitFixed.indexOf(1) === -1); return; } var tr = e.target.closest("tr[data-oi]"); if (tr) { var i = +tr.getAttribute("data-oi"); toast(CO[i].name + ": " + (CO[i].maxDev > 2 ? "макс. отклонение " + CO[i].maxDev + "%" : "в пределах нормы") + (CO[i].name === "ООО Б" ? " · расхождение единиц: ×1/1000" : ""), CO[i].maxDev > 5 ? "warn" : "ok"); } }); v.querySelectorAll("[data-act='to-check']").forEach(function (b) { b.addEventListener("click", function () { if (S.unitFixed.indexOf(1) === -1) { toast("Сначала исправьте единицы ООО Б (клик по ячейке выручки)", "warn"); return; } if (!S.done.check) { S.done.check = true; save(); } showScreen("analysis"); }); }); } // анализ if (screen === "analysis") { var ta = $("anaText"); if (ta) ta.value = analysisText(curA); v.querySelectorAll("[data-ana]").forEach(function (r) { r.addEventListener("click", function () { curA = +r.getAttribute("data-ana"); save(); showScreen("analysis"); }); }); if (ta) ta.addEventListener("input", function () { var cur = S.analysis[curA] || {}; S.analysis[curA] = Object.assign(cur, { text: ta.value, edited: true }); save(); var st = ta.closest(".analysis-body").querySelector(".ed-stats"); if (st) st.textContent = ta.value.split(/\s+/).filter(Boolean).length + " слов"; }); v.querySelectorAll("[data-act='a-prev'],[data-act='a-next']").forEach(function (b) { b.addEventListener("click", function () { curA = (curA + (b.getAttribute("data-act") === "a-next" ? 1 : CO.length - 1)) % CO.length; save(); showScreen("analysis"); }); }); v.querySelectorAll("[data-act='a-accept']").forEach(function (b) { b.addEventListener("click", function () { var st = S.analysis[curA] || {}; st.status = 1; S.analysis[curA] = st; save(); toast(CO[curA].name + ": выводы приняты", "ok"); showScreen("analysis"); }); }); v.querySelectorAll("[data-act='accept-all']").forEach(function (b) { b.addEventListener("click", acceptAllAnalysis); }); v.querySelectorAll("[data-act='make-memo']").forEach(function (b) { b.addEventListener("click", makeMemo); }); } // итог if (screen === "result") { v.querySelectorAll("[data-act='dl-xlsx']").forEach(function (b) { b.addEventListener("click", function () { download(xlsxText(), "Сводный_файл_июнь_2026.txt"); }); }); v.querySelectorAll("[data-act='dl-memo']").forEach(function (b) { b.addEventListener("click", function () { download(memoText(), "Записка_июнь_2026.txt"); }); }); var sb2 = v.querySelector("[data-act='send-pkg']"); if (sb2 && !S.sent) sb2.addEventListener("click", function () { S.sent = true; S.done.result = true; save(); toast("Пакет отправлен финансовому директору · 09:58", "ok"); showScreen("result"); }); } } /* ---------- инициализация ---------- */ function init() { var html = document.documentElement; T = S.theme || "light"; html.setAttribute("data-theme", T); var ti = $("themeIcon"); if (ti) ti.setAttribute("href", T === "light" ? "#moon" : "#sun"); $("themeToggle").addEventListener("click", function () { T = T === "light" ? "dark" : "light"; S.theme = T; save(); html.setAttribute("data-theme", T); if (ti) ti.setAttribute("href", T === "light" ? "#moon" : "#sun"); }); $("burger").hidden = window.innerWidth >= 832; $("burger").addEventListener("click", function () { var sb = $("sidebar"); var open = sb.hasAttribute("data-open"); if (open) closeMobile(); else { sb.setAttribute("data-open", "true"); $("scrim").classList.add("on"); } $("burger").hidden = window.innerWidth >= 832; }); $("scrim").addEventListener("click", closeMobile); window.addEventListener("resize", function () { $("burger").hidden = window.innerWidth >= 832; }); document.querySelector(".kt-ai-shell").addEventListener("click", function (e) { var n = e.target.closest(".kt-ai-nav-item[data-screen]"); if (n) showScreen(n.getAttribute("data-screen")); }); if (S.resetFlag) { delete S.resetFlag; save(); toast("Предыдущее состояние не восстановилось · начат новый цикл", "warn"); } renderSidebar(); showScreen(S.screen || "upload"); } /* ================= ЭКРАН 5 — о решении ================= */ function viewAbout() { return pageHead("О решении", "Сводный финансовый анализ дочерних компаний", "", '') + '
' + card("Паспорт решения", dl([ ["Наименование", "Сводный финансовый анализ дочерних компаний"], ["Исполнитель", "владелец процесса — сборка на платформе Vibe AI"], ["Пользователи", "аналитик, финансовый директор"], ["Частота", "еженедельно, вложения по пятницам"], ["Дедлайн", "понедельник, 15:00"] ])) + card("Предпосылки", '') + card("Входные данные", '') + card("Что делает ИИ", '
  1. Распознаёт формат файла и сопоставляет столбцы
  2. Переносит данные в единый сводный файл
  3. Подсвечивает отклонения от плана
  4. Генерирует черновик анализа по каждой компании
') + card("Что решает человек", '') + card("Результат и эффект", '') + card("Риски и меры", '') + '
'; } function card(title, body) { return '

' + title + '

' + body + '
'; } function dl(rows) { return '
' + rows.map(function (r) { return '
' + r[0] + '
' + r[1] + '
'; }).join("") + '
'; } document.addEventListener("DOMContentLoaded", init); })();