478 lines
31 KiB
HTML
478 lines
31 KiB
HTML
<!doctype html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Оптимизация расходов — презентация</title>
|
||
<script src="https://cdn.jsdelivr.net/npm/pptxgenjs@3.12.0/dist/pptxgen.bundle.js"></script>
|
||
<style>
|
||
* { margin: 0; box-sizing: border-box; }
|
||
body { font-family: "Segoe UI", "Roboto", "Helvetica Neue", -apple-system, sans-serif; background: #0D1B2A; color: #F0F4F8; }
|
||
.slide { display: none; min-height: 100vh; padding: 7vh 8vw; flex-direction: column; justify-content: flex-start; }
|
||
.slide.on { display: flex; }
|
||
.slide h1 { font-size: clamp(28px, 4.6vw, 54px); margin-bottom: 20px; line-height: 1.15; letter-spacing: -.01em; font-weight: 600; color: #F0F4F8; }
|
||
.slide .accent { color: #8EAEC8; }
|
||
.slide .kpi-row { display: flex; gap: 14px; flex-wrap: wrap; margin: 18px 0 14px; }
|
||
.slide .kpi { flex: 1 1 180px; background: #142433; border: 1px solid #1B4F72; border-radius: 10px; padding: 14px 16px; min-width: 160px; }
|
||
.slide .kpi .num { font-size: clamp(22px, 3.4vw, 36px); font-weight: 700; color: #8EAEC8; letter-spacing: -.02em; font-variant-numeric: tabular-nums; line-height: 1; }
|
||
.slide .kpi .lbl { font-size: clamp(11px, 1.4vw, 14px); color: #8899A6; margin-top: 6px; line-height: 1.35; }
|
||
.slide .kpi.warn .num { color: #D4680A; }
|
||
.slide .kpi.risk .num { color: #C0392B; }
|
||
.slide .kpi.ok .num { color: #1A7F4B; }
|
||
.slide ul { list-style: none; margin: 14px 0; display: flex; flex-direction: column; gap: 12px; }
|
||
.slide li { font-size: clamp(15px, 2vw, 22px); line-height: 1.55; padding-left: 28px; position: relative; color: #F0F4F8; }
|
||
.slide li::before { content: ""; position: absolute; left: 0; top: .55em; width: 10px; height: 10px; border-radius: 3px; background: #8EAEC8; }
|
||
.slide li::after { display: none; }
|
||
.slide li.sub { font-size: clamp(12px, 1.5vw, 16px); color: #8899A6; padding-left: 46px; line-height: 1.5; }
|
||
.slide li.sub::before { width: 6px; height: 6px; border-radius: 50%; background: #3A5A70; top: .65em; left: 18px; }
|
||
.slide table { width: 100%; border-collapse: collapse; font-size: clamp(11px, 1.5vw, 15px); margin-top: 10px; }
|
||
.slide th { text-align: left; padding: 8px 10px; background: #142433; color: #8899A6; font-weight: 600; font-size: clamp(10px, 1.3vw, 13px); text-transform: uppercase; letter-spacing: .04em; border-bottom: 1px solid #1B4F72; }
|
||
.slide th.ta-r { text-align: right; }
|
||
.slide td { padding: 8px 10px; border-bottom: 1px solid #1E3A52; color: #E8F0F8; font-variant-numeric: tabular-nums; vertical-align: top; }
|
||
.slide td.ta-r { text-align: right; }
|
||
.slide .pos { color: #C0392B; font-weight: 600; }
|
||
.slide .neg { color: #1A7F4B; font-weight: 600; }
|
||
.slide .dim { color: #6C8FA8; }
|
||
.slide .note { font-size: clamp(11px, 1.4vw, 14px); color: #8899A6; margin-top: 14px; line-height: 1.45; }
|
||
.slide .footer { margin-top: auto; padding-top: 18px; font-size: 12px; color: #6C8FA8; display: flex; gap: 18px; flex-wrap: wrap; }
|
||
.slide .footer .sep { width: 4px; height: 4px; border-radius: 50%; background: #3A5A70; align-self: center; }
|
||
.num-page { position: fixed; bottom: 18px; right: 22px; color: #6C8FA8; font-size: 13px; font-variant-numeric: tabular-nums; }
|
||
.nav { position: fixed; bottom: 14px; left: 22px; display: flex; gap: 8px; z-index: 10; }
|
||
.nav button { border: 1px solid #1B4F72; background: #142433; color: #F0F4F8; border-radius: 8px; padding: 8px 14px; cursor: pointer; font-size: 14px; transition: background .15s; }
|
||
.nav button:hover { background: #1E3A52; }
|
||
.nav .pptx { border-color: #8EAEC8; color: #8EAEC8; }
|
||
.nav .pptx:hover { background: #1B4F72; }
|
||
.title-slide { justify-content: center; }
|
||
.title-slide h0 { font-size: clamp(36px, 6vw, 72px); font-weight: 600; line-height: 1.1; margin-bottom: 12px; letter-spacing: -.02em; }
|
||
.title-slide h2 { font-size: clamp(18px, 2.4vw, 28px); color: #8899A6; font-weight: 400; margin-bottom: 30px; line-height: 1.35; max-width: 80ch; }
|
||
.title-slide .badge { display: inline-block; background: #142433; border: 1px solid #1B4F72; color: #8EAEC8; padding: 4px 12px; border-radius: 20px; font-size: 12px; letter-spacing: .08em; text-transform: uppercase; margin-bottom: 22px; }
|
||
@media print {
|
||
.nav, .num-page { display: none; }
|
||
.slide { display: flex; page-break-after: always; min-height: 100vh; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div id="deck"></div>
|
||
<div class="nav">
|
||
<button onclick="go(-1)">← Назад</button>
|
||
<button onclick="go(1)">Вперёд →</button>
|
||
<button class="pptx" onclick="downloadPptx()">Скачать .pptx</button>
|
||
</div>
|
||
<div class="num-page" id="numPage"></div>
|
||
|
||
<script>
|
||
var SLIDES = [
|
||
// 1
|
||
{ type: 'title',
|
||
title: 'ОПТИМИЗАЦИЯ РАСХОДОВ ПО СЕРВИСНЫМ КОНТРАКТАМ',
|
||
subtitle: 'Анализ детализации 2023 – 1-е полугодие 2026 гг. · 103 позиции · отклонения и периметры',
|
||
chips: ['103 позиции', '2023 · 2024 · 2025 · 1пг 2026', '4 227 млн тг за 1пг 2026', 'Тенге, факт'] },
|
||
|
||
// 2
|
||
{ type: 'kpi', title: 'Итоговый свод',
|
||
kpi: [
|
||
{ num: '9,58', unit: 'млрд тг', lbl: 'Всего за 2023 (55 поз.)', tone: '' },
|
||
{ num: '10,83', unit: 'млрд тг', lbl: 'Всего за 2024 (54 поз.) — пик', tone: 'risk' },
|
||
{ num: '8,35', unit: 'млрд тг', lbl: 'Всего за 2025 (45 поз.)', tone: '' },
|
||
{ num: '4,227', unit: 'млрд тг', lbl: '1пг 2026 (31 поз.)', tone: 'ok' },
|
||
],
|
||
bullets: [
|
||
'2024 → 2025: падение <b class="neg">−2,48 млрд тг (−22,9 %)</b> — сильнее прироста 2024 (+13,1 %), это не экономия, а смена номенклатуры',
|
||
'1пг 2026: <b>4,227 млрд</b> — на <b class="pos">+52 млн тг (+1,3 %)</b> выше темпа 2025, т. е. при сохранении темпа год даст ≈ 8,45 млрд',
|
||
'По годам: 2026 (по темпу ×2) ≈ <b>8,45 млрд</b>, т. е. возврат на уровень 2024',
|
||
],
|
||
footnote: 'Источник: «Расходы по сервисным контрактам.xlsx», факт, 103 позиции.' },
|
||
|
||
// 3
|
||
{ type: 'table', title: 'Топ-10 отклонений: 2025 vs 2024',
|
||
hint: 'Статьи с Δ ≥ 0,1 млрд тг, где есть факт за оба года. Красное — рост, зелёное — снижение.',
|
||
table: [
|
||
['#', 'Позиция', '2024, млн тг', '2025, млн тг', 'Δ, млн тг', 'Δ %'],
|
||
['1', 'Техническая поддержка Ericsson (CS part)', '963,8', '268,9', '<span class="neg">−694,9</span>', '<span class="neg">−72,1 %</span>'],
|
||
['2', 'Техническая поддержка Ericsson (5G part)', '308,1', '134,5', '<span class="neg">−173,6</span>', '<span class="neg">−56,4 %</span>'],
|
||
['3', 'Сервис отправки микровызовов', '283,3', '98,9', '<span class="neg">−184,3</span>', '<span class="neg">−65,1 %</span>'],
|
||
['4', 'SUS подписка vEPC проект', '150,4', '116,3', '<span class="neg">−34,1</span>', '<span class="neg">−22,7 %</span>'],
|
||
['5', 'Мегакам лицензии / vPBX (базовая строка)', '40,7', '86,8', '<span class="pos">+46,1</span>', '<span class="pos">+113,2 %</span>'],
|
||
],
|
||
note: 'Помимо 5 строк с фактом за оба года — 19 новых статей 2025 (3,635 млрд тг): Ericsson 1 057, MS EA 630, MS EA COGS 513, Микровызовы 283, Veritas 257 и т. д.' },
|
||
|
||
// 4
|
||
{ type: 'table', title: '1пг 2026 vs 1пг 2025 — существующие статьи',
|
||
hint: '1пг 2025 = факт 2025 / 2 (ориентир при равномерном распределении). Отклонение ≥ 0,05 млрд тг.',
|
||
table: [
|
||
['Позиция', '1пг 2025, млн', '1пг 2026, млн', 'Δ, млн тг', 'Δ %'],
|
||
['Серверные лицензии MS EA', '315,1', '630,2', '<span class="pos">+315,1</span>', '<span class="pos">+100,0 %</span>'],
|
||
['Техническая поддержка Huawei', '53,7', '171,5', '<span class="pos">+117,8</span>', '<span class="pos">+219,5 %</span>'],
|
||
['Техническая поддержка Checkpoint Diamont', '36,5', '101,4', '<span class="pos">+64,9</span>', '<span class="pos">+177,6 %</span>'],
|
||
['Техническая поддержка Nexign', '630,2', '686,3', '<span class="pos">+56,1</span>', '<span class="pos">+8,9 %</span>'],
|
||
['СОРМ-Ронекс', '56,9', '30,0', '<span class="neg">−26,9</span>', '<span class="neg">−47,3 %</span>'],
|
||
['Aitu Passport (облако УЦ)', '80,2', '58,3', '<span class="neg">−21,9</span>', '<span class="neg">−27,3 %</span>'],
|
||
],
|
||
note: 'Существенные сокращения на фоне роста MS EA: в 1пг 2026 на 103 млн тг больше, чем по темпу 2025 — это рост именно по MS EA +15,7 млн и Checkpoint +64,9 млн.' },
|
||
|
||
// 5
|
||
{ type: 'table', title: 'Новые статьи 1пг 2026 (нет в 2025)',
|
||
hint: '4 статьи, которых не было в 2025 году. Суммарно 1 276 млн тг за 1пг 2026.',
|
||
table: [
|
||
['Позиция', '1пг 2026, млн тг', 'Комментарий'],
|
||
['Техническая поддержка Ericsson (общий новый договор)', '826,6', 'объединила CS part + 5G part'],
|
||
['Техническая поддержка Oracle HW', '376,0', 'новая строка (в 2025 была как Oracle HW 73,5)'],
|
||
['Мегакам лицензии', '49,0', 'лицензии'],
|
||
['Техническая поддержка Cisco smartnet', '25,0', 'встречный контракт'],
|
||
],
|
||
note: 'Ключевой сдвиг: Ericsson (общий новый договор) 827 млн тг за 1пг 2026 — это объединение двух старых строк, т. е. периметр сменился, а не появился новый расход.' },
|
||
|
||
// 6
|
||
{ type: 'two', title: 'Номенклатура 2025: что ушло и что пришло',
|
||
tableA: {
|
||
title: 'Появились в 2025 (нет в 2024)',
|
||
rows: [
|
||
['Ericsson', '1 057,0', ''],
|
||
['MS EA (базовая)', '630,2', ''],
|
||
['MS EA / 3 транш / COGS', '513,4', ''],
|
||
['Сервис отправки микровызовов', '283,3', ''],
|
||
['Veritas', '257,4', ''],
|
||
['SUS подписка vEPC проект', '150,4', ''],
|
||
['Huawei', '107,3', ''],
|
||
['Steering of Roaming, SMSW', '106,0', ''],
|
||
['…и ещё 11 позиций', '154,0', ''],
|
||
['Итого новых: 19 шт.', '3 635,2', ''],
|
||
]
|
||
},
|
||
tableB: {
|
||
title: 'Исчезли в 2025 (были в 2024)',
|
||
rows: [
|
||
['FLM Expenses', '3 481,0', '→ FLM (3 232 млн)'],
|
||
['Прочие', '500,8', ''],
|
||
['Video Conference System (Cisco)', '446,7', ''],
|
||
['Veritas', '132,2', ''],
|
||
['System SUPPORT of ZTE', '80,3', ''],
|
||
['Услуги тех. поддержки HP', '76,9', ''],
|
||
['Ремонт ФА3 гр. (Dell)', '69,5', ''],
|
||
['Maintenance FOCL (ВОЛС)', '55,1', ''],
|
||
['…и ещё 19 позиций', '270,5', ''],
|
||
['Итого исчезнувших: 29 шт.', '5 142,2', ''],
|
||
]
|
||
},
|
||
note: 'Чистая смена номенклатуры: пришло 3635 млн, ушло 5142 млн. Разница −1507 млн тг — это механика спада 2025.' },
|
||
|
||
// 7
|
||
{ type: 'text', title: 'Эффективный анализ статей 2025',
|
||
bullets: [
|
||
'<b>FLM: 3 481 млн (2024) → 3 232 млн (2025) → 2 184 млн (1пг 2026)</b>. В 2025 перенесено из «FLM Expenses» в «FLM».',
|
||
'Внутри FLM: 1пг 2026 vs 1пг 2025 — <b class="pos">+568 млн тг (+35,1 %)</b>. Статья растёт.',
|
||
'<b>Ericsson суммарно: 1 186 млн (2024) → 790 млн (2025)</b> — минус 33%. При этом 5G часть перешла в «общий новый договор» 2026 (827 млн за 1пг 2026).',
|
||
'<b>MS EA (базовая строка): 630 млн тг за 1пг 2026 = как за весь 2025</b>. При сохранении темпа год даст ≈ 1 260 млн (+100 %).',
|
||
'Строки «MS EA / 3 транш / COGS» (513 млн) и «G&A» (45 млн) в 2026 не фигурируют — сверить периметр договоров.',
|
||
'Сокращения в 1пг 2026: CORM-Ронекс −26,9 млн, Aitu Passport −21,9 млн, Nехign −68 млн (к темпу 2025).',
|
||
] },
|
||
|
||
// 8
|
||
{ type: 'recommendations', title: 'Рекомендации',
|
||
bullets: [
|
||
{ b: 'Приоритет 1 — сверить периметры 1пг 2026.', s: 'MS EA (базовая = как за весь 2025), Oracle HW (376 млн, новая), Ericsson (общий новый договор, 827 млн, объединение).', tone: 'risk' },
|
||
{ b: 'Приоритет 2 — консолидация Ericsson.', s: 'Объединение CS part + 5G part уже сделано в 2026. Следующий шаг — переход на гибридную модель (P1 + RMA за вендора, P2/P3 силами КТ), как в референсе ОДС.', tone: 'warn' },
|
||
{ b: 'Приоритет 3 — проверить FLM.', s: '+35,1 % к темпу 2025. 2 184 млн за 1пг 2026. Если рост продолжается — пересмотреть условия.', tone: 'warn' },
|
||
{ b: 'Приоритет 4 — зафиксировать сокращения.', s: 'CORM-Ронекс −26,9 млн, Aitu Passport −21,9 млн, Nехign −68 млн (к темпу 2025). Заложить в план 2027.', tone: 'ok' },
|
||
{ b: 'Приоритет 5 — реестр статусов.', s: 'К каждой статье: «прямой / посредник / дистрибьютор», «готов к инсорсингу / нужен вендор», P1/RMA за период.', tone: '' },
|
||
] },
|
||
|
||
// 9
|
||
{ type: 'close', title: 'Итого',
|
||
big: '−2,48 млрд тг\n(−22,9 %)\nк 2025',
|
||
bullets: [
|
||
'Падение 2025 — не экономия, а смена номенклатуры (FLM Expenses → FLM, Ericsson part → общий договор, MS EA COGS/G&A).',
|
||
'1пг 2026 показывает <b>рост +1,3 % к темпу 2025</b>: драйверы — MS EA +15,7 млн, Checkpoint +64,9 млн.',
|
||
'Главный риск 2027 — <b>MS EA</b>: 630 млн за 1пг 2026 = как за весь 2025. При сохранении темпа год ≈ +100 %.',
|
||
'Следующий шаг — сверить периметры договоров и зафиксировать реестр статусов (прямой / посредник, инсорсинг / вендор).',
|
||
],
|
||
footnote: 'Итоговый отчёт (Excel) + полная детализация 103 позиций прилагаются.' },
|
||
];
|
||
|
||
var cur = 0;
|
||
|
||
function render() {
|
||
var html = SLIDES.map(function (s, i) {
|
||
var on = i === cur ? ' on' : '';
|
||
var inner = renderSlide(s);
|
||
return '<section class="slide' + on + '"' + (s.type === 'title' || s.type === 'close' ? ' data-center="1"' : '') + '>' + inner + '</section>';
|
||
}).join('');
|
||
document.getElementById('deck').innerHTML = html;
|
||
document.getElementById('numPage').textContent = (cur + 1) + ' / ' + SLIDES.length;
|
||
}
|
||
|
||
function renderSlide(s) {
|
||
if (s.type === 'title') {
|
||
return '<span class="badge">' + esc(s.subtitle.split(' · ')[0]) + '</span>' +
|
||
'<h0>' + esc(s.title) + '</h0>' +
|
||
'<h2>' + esc(s.subtitle) + '</h2>' +
|
||
'<div class="kpi-row">' + s.chips.map(function (c) {
|
||
return '<div class="kpi"><div class="num" style="font-size:20px">' + esc(c) + '</div></div>';
|
||
}).join('') + '</div>' +
|
||
footer();
|
||
}
|
||
if (s.type === 'kpi') {
|
||
return '<div class="kpi-row">' + s.kpi.map(function (k) {
|
||
return '<div class="kpi ' + (k.tone || '') + '"><div class="num">' + esc(k.num) + ' ' + esc(k.unit || '') + '</div><div class="lbl">' + esc(k.lbl) + '</div></div>';
|
||
}).join('') + '</div>' +
|
||
'<ul>' + s.bullets.map(function (b) { return '<li>' + b + '</li>'; }).join('') + '</ul>' +
|
||
'<div class="note">' + esc(s.footnote || '') + '</div>' + footer();
|
||
}
|
||
if (s.type === 'table') {
|
||
var th = s.table[0].map(function (h, i) {
|
||
return '<th' + (i > 0 ? ' class="ta-r"' : '') + '>' + esc(h) + '</th>';
|
||
}).join('');
|
||
var body = s.table.slice(1).map(function (row) {
|
||
return '<tr>' + row.map(function (c, i) {
|
||
return '<td' + (i > 0 ? ' class="ta-r"' : '') + '>' + c + '</td>';
|
||
}).join('') + '</tr>';
|
||
}).join('');
|
||
return '<h1>' + esc(s.title) + '</h1>' +
|
||
'<div class="note" style="margin-top:0">' + esc(s.hint || '') + '</div>' +
|
||
'<table><thead><tr>' + th + '</tr></thead><tbody>' + body + '</tbody></table>' +
|
||
(s.note ? '<div class="note">' + esc(s.note) + '</div>' : '') + footer();
|
||
}
|
||
if (s.type === 'two') {
|
||
function mini(t) {
|
||
var rows = t.rows.map(function (r) {
|
||
return '<tr><td>' + esc(r[0]) + '</td><td class="ta-r">' + esc(r[1]) + '</td><td class="ta-r dim" style="text-align:left;width:35%;font-weight:400">' + (r[2] ? esc(r[2]) : '') + '</td></tr>';
|
||
}).join('');
|
||
return '<div style="flex:1 1 45%;min-width:300px"><table><thead><tr><th>' + esc(t.title) + '</th><th class="ta-r">млн тг</th><th class="ta-r"></th></tr></thead><tbody>' + rows + '</tbody></table></div>';
|
||
}
|
||
return '<h1>' + esc(s.title) + '</h1>' +
|
||
'<div style="display:flex;gap:20px;flex-wrap:wrap;flex:1">' + mini(s.tableA) + mini(s.tableB) + '</div>' +
|
||
(s.note ? '<div class="note">' + esc(s.note) + '</div>' : '') + footer();
|
||
}
|
||
if (s.type === 'text') {
|
||
return '<h1>' + esc(s.title) + '</h1>' +
|
||
'<ul>' + s.bullets.map(function (b) { return '<li>' + b + '</li>'; }).join('') + '</ul>' + footer();
|
||
}
|
||
if (s.type === 'recommendations') {
|
||
var items = s.bullets.map(function (b) {
|
||
var cls = b.tone === 'risk' ? 'kpi risk' : b.tone === 'warn' ? 'kpi warn' : b.tone === 'ok' ? 'kpi ok' : 'kpi';
|
||
return '<div class="' + cls + '" style="margin-bottom:10px"><div class="num" style="font-size:15px;font-weight:600;color:inherit">' + b.b + '</div><div class="lbl" style="margin-top:4px;font-size:13px">' + b.s + '</div></div>';
|
||
}).join('');
|
||
return '<h1>' + esc(s.title) + '</h1>' + items + footer();
|
||
}
|
||
if (s.type === 'close') {
|
||
return '<h1>' + esc(s.title) + '</h1>' +
|
||
'<div class="kpi-row" style="flex-direction:row;align-items:center">' +
|
||
'<div class="kpi" style="flex:0 0 260px;border-width:2px;border-color:#8EAEC8"><div class="num" style="font-size:40px;line-height:1.1;white-space:pre-wrap">' + esc(s.big) + '</div><div class="lbl">Итог 2025 vs 2024</div></div>' +
|
||
'<div style="flex:1"><ul style="margin:0">' + s.bullets.map(function (b) { return '<li>' + b + '</li>'; }).join('') + '</ul>' +
|
||
'<div class="note">' + esc(s.footnote || '') + '</div></div></div>' + footer();
|
||
}
|
||
return '';
|
||
}
|
||
|
||
function footer() {
|
||
return '<div class="footer"><span>Оптимизация расходов</span><span class="sep"></span><span>103 позиции · 2023 – 1пг 2026</span><span class="sep"></span><span>Источник: Расходы по сервисным контрактам.xlsx</span></div>';
|
||
}
|
||
|
||
function esc(s) {
|
||
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||
}
|
||
|
||
function go(d) {
|
||
cur = Math.min(SLIDES.length - 1, Math.max(0, cur + d));
|
||
render();
|
||
}
|
||
document.addEventListener('keydown', function (e) {
|
||
if (e.key === 'ArrowRight' || e.key === ' ' || e.key === 'PageDown') go(1);
|
||
if (e.key === 'ArrowLeft' || e.key === 'PageUp') go(-1);
|
||
});
|
||
|
||
// ============ PPTX ============
|
||
// Палитра из референса
|
||
var C = { bg: '0D1B2A', panel: '142433', border: '1B4F72', accent: '8EAEC8', mute: '8899A6', dim: '6C8FA8', ok: '1A7F4B', warn: 'D4680A', risk: 'C0392B', fg: 'F0F4F8', line: '1E3A52' };
|
||
|
||
function downloadPptx() {
|
||
var p = new PptxGenJS();
|
||
p.defineLayout({ name: 'W', width: 13.33, height: 7.5 });
|
||
p.layout = 'W';
|
||
p.author = 'Vibe42';
|
||
p.company = 'Анализ сервисных контрактов';
|
||
|
||
function addBg(sl) {
|
||
sl.background = { color: C.bg };
|
||
sl.addText('Оптимизация расходов · 103 позиции · 2023 – 1пг 2026', { x: 0.5, y: 7.0, w: 12, h: 0.4, fontSize: 9, color: C.dim, fontFace: 'Segoe UI' });
|
||
}
|
||
function addTitle(sl, text, opts) {
|
||
sl.addText(text, Object.assign({ x: 0.5, y: 0.35, w: 12, h: 0.9, fontSize: 30, bold: true, color: C.fg, fontFace: 'Segoe UI' }, opts || {}));
|
||
}
|
||
function addKpiRow(sl, items, y) {
|
||
var n = items.length;
|
||
var w = 12.4 / n;
|
||
items.forEach(function (k, i) {
|
||
var x = 0.5 + i * w;
|
||
sl.addShape(p.ShapeType.roundRect, { x: x, y: y, w: w - 0.12, h: 1.4, fill: { color: C.panel }, line: { color: C.border, width: 1 }, rectRadius: 0.06 });
|
||
var tone = k.tone === 'risk' ? C.risk : k.tone === 'warn' ? C.warn : k.tone === 'ok' ? C.ok : C.accent;
|
||
sl.addText(k.num + (k.unit ? ' ' + k.unit : ''), { x: x + 0.15, y: y + 0.15, w: w - 0.4, h: 0.6, fontSize: 24, bold: true, color: tone, fontFace: 'Segoe UI' });
|
||
sl.addText(k.lbl, { x: x + 0.15, y: y + 0.75, w: w - 0.4, h: 0.6, fontSize: 10, color: C.mute, fontFace: 'Segoe UI' });
|
||
});
|
||
}
|
||
function addBullets(sl, bullets, opts) {
|
||
sl.addText(bullets.map(function (b, idx) {
|
||
return { text: b, options: { bullet: { code: '2022', indent: 14 }, color: C.fg, breakLine: true, paraSpaceAfter: 8 } };
|
||
}), Object.assign({ x: 0.5, y: 2.0, w: 12.4, h: 4.8, fontSize: 14, fontFace: 'Segoe UI', color: C.fg }, opts || {}));
|
||
}
|
||
function addRowsTable(sl, headers, rows, opts) {
|
||
var header = headers.map(function (h) { return { text: h, options: { bold: true, color: '8899A6', fill: { color: C.panel }, fontSize: 11, valign: 'middle' } }; });
|
||
var body = rows.map(function (row, ri) {
|
||
return row.map(function (cell, ci) {
|
||
var color = C.fg;
|
||
var bold = false;
|
||
if (typeof cell === 'string') {
|
||
if (cell.indexOf('class="pos"') >= 0 || (cell.indexOf('+') === 0 && ci > 0)) { color = C.risk; bold = true; }
|
||
if (cell.indexOf('class="neg"') >= 0 || (ci > 0 && cell.indexOf('−') === 0)) { color = C.ok; bold = true; }
|
||
cell = cell.replace(/<[^>]+>/g, '');
|
||
}
|
||
return { text: String(cell), options: { color: color, bold: bold, fill: { color: ri % 2 ? C.bg : '101D2A' }, fontSize: 11 } };
|
||
});
|
||
});
|
||
var table = [header].concat(body);
|
||
sl.addTable(table, Object.assign({ x: 0.5, y: 2.4, w: 12.4, fontFace: 'Segoe UI', rowH: 0.4, autoPage: false, valign: 'middle' }, opts || {}));
|
||
}
|
||
|
||
// 1: title
|
||
(function () {
|
||
var s = SLIDES[0];
|
||
var sl = p.addSlide(); addBg(sl);
|
||
sl.addShape(p.ShapeType.rect, { x: 0.5, y: 0.5, w: 0.35, h: 0.35, fill: { color: C.accent } });
|
||
sl.addText(s.subtitle, { x: 1.0, y: 0.5, w: 12, h: 0.5, fontSize: 12, color: C.accent, fontFace: 'Segoe UI', charSpacing: 4 });
|
||
sl.addText(s.title, { x: 0.5, y: 2.2, w: 12.4, h: 1.8, fontSize: 50, bold: true, color: C.fg, fontFace: 'Segoe UI', charSpacing: -1 });
|
||
var chips = s.chips.map(function (c) { return { text: c, options: { bullet: false, color: C.accent, breakLine: true } }; });
|
||
sl.addText(chips, { x: 0.5, y: 4.6, w: 12.4, h: 1.6, fontSize: 14, fontFace: 'Segoe UI' });
|
||
})();
|
||
|
||
// 2: KPI + bullets
|
||
(function () {
|
||
var s = SLIDES[1];
|
||
var sl = p.addSlide(); addBg(sl); addTitle(sl, s.title);
|
||
addKpiRow(sl, s.kpi, 1.5);
|
||
sl.addText(s.bullets.map(function (b) {
|
||
return { text: b.replace(/<[^>]+>/g, ''), options: { bullet: { code: '2022', indent: 14 }, color: C.fg, breakLine: true, paraSpaceAfter: 10 } };
|
||
}), { x: 0.5, y: 3.3, w: 12.4, h: 2.8, fontSize: 14, fontFace: 'Segoe UI' });
|
||
sl.addText(s.footnote, { x: 0.5, y: 6.2, w: 12.4, h: 0.4, fontSize: 10, color: C.mute, italic: true, fontFace: 'Segoe UI' });
|
||
})();
|
||
|
||
// 3: dev25
|
||
(function () {
|
||
var s = SLIDES[2];
|
||
var sl = p.addSlide(); addBg(sl); addTitle(sl, s.title);
|
||
sl.addText(s.hint, { x: 0.5, y: 1.3, w: 12.4, h: 0.4, fontSize: 11, color: C.mute, italic: true, fontFace: 'Segoe UI' });
|
||
var headers = s.table[0].map(function (h, i) { return { text: h, options: { bold: true, color: '8899A6', fill: { color: C.panel }, fontSize: 11, valign: 'middle' } }; });
|
||
var rows = s.table.slice(1).map(function (row, ri) {
|
||
return row.map(function (cell, ci) {
|
||
var color = C.fg, bold = false, txt = String(cell).replace(/<[^>]+>/g, '');
|
||
if (ci === 4) { color = txt.indexOf('+') === 0 ? C.risk : C.ok; bold = true; }
|
||
if (ci === 5) { color = txt.indexOf('+') === 0 ? C.risk : C.ok; bold = true; }
|
||
if (ri % 2 === 1) return { text: txt, options: { color: color, bold: bold, fill: { color: '101D2A' }, fontSize: 11 } };
|
||
return { text: txt, options: { color: color, bold: bold, fontSize: 11 } };
|
||
});
|
||
});
|
||
sl.addTable([headers].concat(rows), { x: 0.5, y: 1.9, w: 12.4, fontFace: 'Segoe UI', rowH: 0.55, autoPage: false, valign: 'middle' });
|
||
sl.addText(s.note, { x: 0.5, y: 5.6, w: 12.4, h: 0.9, fontSize: 11, color: C.mute, fontFace: 'Segoe UI' });
|
||
})();
|
||
|
||
// 4: dev26
|
||
(function () {
|
||
var s = SLIDES[3];
|
||
var sl = p.addSlide(); addBg(sl); addTitle(sl, s.title);
|
||
sl.addText(s.hint, { x: 0.5, y: 1.3, w: 12.4, h: 0.4, fontSize: 11, color: C.mute, italic: true, fontFace: 'Segoe UI' });
|
||
var headers = s.table[0].map(function (h, i) { return { text: h, options: { bold: true, color: '8899A6', fill: { color: C.panel }, fontSize: 11, valign: 'middle' } }; });
|
||
var rows = s.table.slice(1).map(function (row, ri) {
|
||
return row.map(function (cell, ci) {
|
||
var color = C.fg, bold = false, txt = String(cell).replace(/<[^>]+>/g, '');
|
||
if (ci === 3 || ci === 4) { color = txt.indexOf('+') === 0 ? C.risk : C.ok; bold = true; }
|
||
if (ri % 2 === 1) return { text: txt, options: { color: color, bold: bold, fill: { color: '101D2A' }, fontSize: 11 } };
|
||
return { text: txt, options: { color: color, bold: bold, fontSize: 11 } };
|
||
});
|
||
});
|
||
sl.addTable([headers].concat(rows), { x: 0.5, y: 1.9, w: 12.4, fontFace: 'Segoe UI', rowH: 0.5, autoPage: false, valign: 'middle' });
|
||
sl.addText(s.note, { x: 0.5, y: 5.4, w: 12.4, h: 0.9, fontSize: 11, color: C.mute, fontFace: 'Segoe UI' });
|
||
})();
|
||
|
||
// 5: new26
|
||
(function () {
|
||
var s = SLIDES[4];
|
||
var sl = p.addSlide(); addBg(sl); addTitle(sl, s.title);
|
||
sl.addText(s.hint, { x: 0.5, y: 1.3, w: 12.4, h: 0.4, fontSize: 11, color: C.mute, italic: true, fontFace: 'Segoe UI' });
|
||
var headers = s.table[0].map(function (h, i) { return { text: h, options: { bold: true, color: '8899A6', fill: { color: C.panel }, fontSize: 11, valign: 'middle' } }; });
|
||
var rows = s.table.slice(1).map(function (row, ri) {
|
||
return row.map(function (cell, ci) {
|
||
if (ci === 1) return { text: String(cell), options: { bold: true, color: C.accent, fill: ri % 2 ? { color: '101D2A' } : undefined, fontSize: 12 } };
|
||
return { text: String(cell), options: { fill: ri % 2 ? { color: '101D2A' } : undefined, fontSize: 11 } };
|
||
});
|
||
});
|
||
sl.addTable([headers].concat(rows), { x: 0.5, y: 1.9, w: 12.4, fontFace: 'Segoe UI', rowH: 0.6, autoPage: false, valign: 'middle' });
|
||
sl.addText(s.note, { x: 0.5, y: 4.8, w: 12.4, h: 1.2, fontSize: 11, color: C.mute, fontFace: 'Segoe UI' });
|
||
})();
|
||
|
||
// 6: two
|
||
(function () {
|
||
var s = SLIDES[5];
|
||
var sl = p.addSlide(); addBg(sl); addTitle(sl, s.title);
|
||
function mini(t, x) {
|
||
sl.addText(t.title, { x: x, y: 1.5, w: 6.0, h: 0.4, fontSize: 12, bold: true, color: C.accent, fontFace: 'Segoe UI' });
|
||
var header = [{ text: '', options: { bold: true, color: '8899A6', fill: { color: C.panel }, fontSize: 10 } }, { text: 'млн тг', options: { bold: true, color: '8899A6', fill: { color: C.panel }, fontSize: 10, valign: 'middle', align: 'right' } }, { text: '', options: { bold: true, color: '8899A6', fill: { color: C.panel }, fontSize: 10 } }];
|
||
var rows = t.rows.map(function (row, ri) {
|
||
var bg = ri % 2 ? { color: '101D2A' } : undefined;
|
||
var isTotal = ri === t.rows.length - 1;
|
||
return [
|
||
{ text: row[0], options: { bold: isTotal, fill: bg, fontSize: 10 } },
|
||
{ text: row[1], options: { bold: isTotal, align: 'right', fill: bg, fontSize: 10 } },
|
||
{ text: row[2] || '', options: { color: C.dim, fill: bg, fontSize: 10 } },
|
||
];
|
||
});
|
||
sl.addTable([header].concat(rows), { x: x, y: 1.95, w: 6.0, fontFace: 'Segoe UI', rowH: 0.35, autoPage: false, valign: 'middle' });
|
||
}
|
||
mini(s.tableA, 0.5);
|
||
mini(s.tableB, 6.8);
|
||
sl.addText(s.note, { x: 0.5, y: 5.8, w: 12.4, h: 0.6, fontSize: 11, color: C.mute, fontFace: 'Segoe UI' });
|
||
})();
|
||
|
||
// 7: text
|
||
(function () {
|
||
var s = SLIDES[6];
|
||
var sl = p.addSlide(); addBg(sl); addTitle(sl, s.title);
|
||
sl.addText(s.bullets.map(function (b) {
|
||
return { text: b.replace(/<[^>]+>/g, ''), options: { bullet: { code: '2022', indent: 14 }, color: C.fg, breakLine: true, paraSpaceAfter: 12 } };
|
||
}), { x: 0.5, y: 1.7, w: 12.4, h: 4.8, fontSize: 14, fontFace: 'Segoe UI' });
|
||
})();
|
||
|
||
// 8: recommendations
|
||
(function () {
|
||
var s = SLIDES[7];
|
||
var sl = p.addSlide(); addBg(sl); addTitle(sl, s.title);
|
||
var y = 1.55;
|
||
s.bullets.forEach(function (b) {
|
||
var tone = b.tone === 'risk' ? C.risk : b.tone === 'warn' ? C.warn : b.tone === 'ok' ? C.ok : C.accent;
|
||
sl.addShape(p.ShapeType.roundRect, { x: 0.5, y: y, w: 12.4, h: 0.85, fill: { color: C.panel }, line: { color: C.border, width: 1 }, rectRadius: 0.05 });
|
||
sl.addShape(p.ShapeType.rect, { x: 0.5, y: y, w: 0.08, h: 0.85, fill: { color: tone } });
|
||
sl.addText(b.b, { x: 0.75, y: y + 0.05, w: 12, h: 0.4, fontSize: 13, bold: true, color: C.fg, fontFace: 'Segoe UI' });
|
||
sl.addText(b.s, { x: 0.75, y: y + 0.45, w: 12, h: 0.35, fontSize: 10, color: C.mute, fontFace: 'Segoe UI' });
|
||
y += 1.0;
|
||
});
|
||
})();
|
||
|
||
// 9: close
|
||
(function () {
|
||
var s = SLIDES[8];
|
||
var sl = p.addSlide(); addBg(sl); addTitle(sl, s.title);
|
||
sl.addShape(p.ShapeType.roundRect, { x: 0.5, y: 1.7, w: 4.4, h: 4.4, fill: { color: C.panel }, line: { color: C.accent, width: 2 }, rectRadius: 0.15 });
|
||
sl.addText(s.big, { x: 0.7, y: 2.2, w: 4.0, h: 2.0, fontSize: 30, bold: true, color: C.accent, fontFace: 'Segoe UI', paraSpaceBefore: 6, lineSpacingMultiple: 1.2 });
|
||
sl.addText('Итог 2025 vs 2024', { x: 0.7, y: 4.4, w: 4.0, h: 0.6, fontSize: 12, color: C.mute, fontFace: 'Segoe UI' });
|
||
sl.addText(s.bullets.map(function (b) {
|
||
return { text: b.replace(/<[^>]+>/g, ''), options: { bullet: { code: '2022', indent: 14 }, color: C.fg, breakLine: true, paraSpaceAfter: 10 } };
|
||
}), { x: 5.3, y: 1.7, w: 7.4, h: 4.4, fontSize: 13, fontFace: 'Segoe UI' });
|
||
sl.addText(s.footnote, { x: 5.3, y: 6.1, w: 7.4, h: 0.5, fontSize: 10, color: C.mute, italic: true, fontFace: 'Segoe UI' });
|
||
})();
|
||
|
||
p.writeFile({ fileName: "Оптимизация_расходов_2023-1пг26.pptx" });
|
||
}
|
||
render();
|
||
</script>
|
||
</body>
|
||
</html>
|