Compare commits

...

1 Commits
main ... pages

View File

@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Учёт кабельной продукции</title>
<title>Сравнение закупочного плана</title>
<link rel="stylesheet" href="design-system/kt-ai-fonts.css">
<link rel="stylesheet" href="design-system/kt-ai-tokens.css">
<link rel="stylesheet" href="design-system/kt-ai-components.css">
@ -23,10 +23,30 @@
.kt-ai-table tr:hover td { background: var(--kt-ai-bg-soft); }
.kt-ai-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.kt-ai-table .actions { white-space: nowrap; }
.kt-ai-table tr.selected { background: var(--kt-ai-bg-soft); }
.kt-ai-table tr.excluded { opacity: 0.5; background: var(--kt-ai-bg-soft); }
.kt-ai-table tr.excluded td { text-decoration: line-through; }
.empty-state { text-align: center; padding: var(--kt-ai-space-12) var(--kt-ai-space-4); color: var(--kt-ai-fg-muted); }
.form-section { padding: var(--kt-ai-space-6) 0; }
.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--kt-ai-space-4); max-width: 800px; }
.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--kt-ai-space-4); max-width: 900px; }
.filter-note { font-size: var(--kt-ai-text-xs); color: var(--kt-ai-fg-faint); margin-left: var(--kt-ai-space-2); }
.compare-panel { display: none; padding: var(--kt-ai-space-6) 0; }
.compare-panel.active { display: block; }
.compare-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--kt-ai-space-4); }
.compare-card { background: var(--kt-ai-card-bg); border: 1px solid var(--kt-ai-card-border); border-radius: var(--kt-ai-radius-xl); padding: var(--kt-ai-space-5); }
.compare-card h4 { margin: 0 0 var(--kt-ai-space-3) 0; font-size: var(--kt-ai-text-lg); }
.compare-row { display: flex; justify-content: space-between; padding: var(--kt-ai-space-2) 0; border-bottom: 1px solid var(--kt-ai-border-strong); }
.compare-row:last-child { border-bottom: none; }
.compare-label { color: var(--kt-ai-fg-muted); }
.compare-value { font-weight: var(--kt-ai-weight-medium); }
.diff-highlight { background: var(--kt-ai-bg-soft); padding: var(--kt-ai-space-1) var(--kt-ai-space-2); border-radius: var(--kt-ai-radius-md); }
.decision-section { padding: var(--kt-ai-space-6) 0; border-top: 1px solid var(--kt-ai-border); margin-top: var(--kt-ai-space-6); }
.status-badge { display: inline-block; padding: var(--kt-ai-space-1) var(--kt-ai-space-3); border-radius: var(--kt-ai-radius-full); font-size: var(--kt-ai-text-sm); font-weight: var(--kt-ai-weight-medium); }
.status-new { background: var(--kt-ai-bg-soft); color: var(--kt-ai-fg); }
.status-review { background: var(--kt-ai-bg-soft); color: var(--kt-ai-fg-muted); }
.status-approved { background: var(--kt-ai-bg-soft); color: var(--kt-ai-fg); }
.status-rejected { background: var(--kt-ai-bg-soft); color: var(--kt-ai-fg-muted); }
.toolbar { display: flex; gap: var(--kt-ai-space-3); align-items: center; margin-bottom: var(--kt-ai-space-4); }
</style>
</head>
<body class="kt-ai-app">
@ -34,9 +54,10 @@
<div class="kt-ai-wrap">
<header class="app-header">
<img src="https://code.vibe42.kz/tore/aitu/kt-logo.svg" alt="Казахтелеком" height="28">
<span class="app-title">Учёт кабельной продукции</span>
<span class="app-title">Сравнение закупочного плана</span>
<span class="kt-ai-spacer"></span>
<span class="kt-ai-chip" data-tone="orange">Демо-данные</span>
<span class="status-badge status-new" id="header-status">Новые позиции</span>
</header>
</div>
@ -46,22 +67,22 @@
<div class="kt-ai-kpi">
<div class="value" id="kpi-total">0</div>
<div class="label">Всего</div>
<div class="hint">кабельных позиций</div>
<div class="hint">позиций в плане</div>
</div>
<div class="kt-ai-kpi">
<div class="value" id="kpi-in-work">0</div>
<div class="label">В работе</div>
<div class="hint">активных задач</div>
<div class="value" id="kpi-groups">0</div>
<div class="label">Группы</div>
<div class="hint">похожих товаров</div>
</div>
<div class="kt-ai-kpi">
<div class="value" id="kpi-ready">0</div>
<div class="label">Готово</div>
<div class="hint">завершено</div>
<div class="value" id="kpi-excluded">0</div>
<div class="label">Исключено</div>
<div class="hint">строк отклонено</div>
</div>
<div class="kt-ai-kpi">
<div class="value" id="kpi-meters">0</div>
<div class="label">Метров</div>
<div class="hint">общая длина</div>
<div class="value" id="kpi-sum">0</div>
<div class="label">Сумма, ₸</div>
<div class="hint">общая стоимость</div>
</div>
</div>
</div>
@ -70,13 +91,13 @@
<section class="filters-section">
<div class="kt-ai-wrap">
<div class="kt-ai-field" style="min-width: 180px;">
<label>Тип кабеля</label>
<select class="kt-ai-select" id="filter-type">
<option value="">Все типы</option>
<option value="Силовой">Силовой</option>
<option value="Оптоволоконный">Оптоволоконный</option>
<option value="Коаксиальный">Коаксиальный</option>
<option value="Контрольный">Контрольный</option>
<label>Категория</label>
<select class="kt-ai-select" id="filter-category">
<option value="">Все категории</option>
<option value="Кабель силовой">Кабель силовой</option>
<option value="Кабель оптоволоконный">Кабель оптоволоконный</option>
<option value="Оборудование">Оборудование</option>
<option value="Материалы">Материалы</option>
</select>
</div>
<div class="kt-ai-field" style="min-width: 160px;">
@ -84,37 +105,51 @@
<select class="kt-ai-select" id="filter-status">
<option value="">Все статусы</option>
<option value="new">Новый</option>
<option value="in-work">В работе</option>
<option value="ready">Готово</option>
<option value="archived">Архив</option>
<option value="review">На согласовании</option>
<option value="approved">Утверждено</option>
<option value="rejected">Отклонено</option>
</select>
<span class="filter-note" data-kt-filter-note>в демо не меняет KPI</span>
</div>
<div class="kt-ai-field" style="min-width: 200px;">
<label>Поиск</label>
<input class="kt-ai-input" id="filter-search" placeholder="Название или сечение...">
<label>Период</label>
<select class="kt-ai-select" id="filter-period">
<option value="all">За всё время</option>
<option value="2026-09">Сентябрь 2026</option>
<option value="2026-10">Октябрь 2026</option>
<option value="2026-Q4">4 квартал 2026</option>
</select>
</div>
<span class="kt-ai-spacer"></span>
<button class="kt-ai-btn" data-variant="ghost" id="btn-reset">Сбросить</button>
<button class="kt-ai-btn" data-variant="primary" id="btn-compare" disabled>Сравнить выбранные</button>
<button class="kt-ai-btn" data-variant="ghost" id="btn-export">Выгрузить Excel</button>
</div>
</section>
<section class="table-section">
<div class="kt-ai-wrap">
<div class="toolbar">
<span style="color: var(--kt-ai-fg-muted); font-size: var(--kt-ai-text-sm);">Выделите 2-4 похожих строки для сравнения (Ctrl+клик)</span>
</div>
<div class="kt-ai-table-wrap">
<table class="kt-ai-table">
<thead>
<tr>
<th>Название</th>
<th>Тип</th>
<th class="num">Сечение, мм²</th>
<th class="num">Длина, м</th>
<th style="width: 40px;">☐</th>
<th>Наименование</th>
<th>Категория</th>
<th class="num">Кол-во</th>
<th class="num">Ед. изм.</th>
<th class="num">Цена, ₸</th>
<th class="num">Сумма, ₸</th>
<th>Срок поставки</th>
<th>Поставщик</th>
<th>Статус</th>
<th>Дата</th>
<th class="actions">Действия</th>
</tr>
</thead>
<tbody id="cable-table-body"></tbody>
<tbody id="plan-table-body"></tbody>
</table>
</div>
<div class="kt-ai-empty" id="empty-state" style="display: none;">
@ -126,106 +161,164 @@
</div>
</section>
<section class="compare-panel" id="compare-panel">
<div class="kt-ai-wrap">
<div class="kt-ai-section-head">
<h2>Сравнение позиций</h2>
<p>Выберите оптимальный вариант или исключите неподходящие</p>
</div>
<div class="compare-grid" id="compare-cards"></div>
<div class="decision-section">
<h3 style="margin-bottom: var(--kt-ai-space-4);">Решение по группе</h3>
<form id="decision-form" class="form-grid">
<div class="kt-ai-field" style="grid-column: 1 / -1;">
<label>Комментарий к решению *</label>
<textarea class="kt-ai-textarea" name="comment" required rows="3" placeholder="Обоснуйте выбор или причину исключения..."></textarea>
</div>
<div class="kt-ai-field">
<label>Решение</label>
<select class="kt-ai-select" name="decision" required>
<option value="">Выберите решение</option>
<option value="approve-best">Утвердить лучший вариант</option>
<option value="exclude-all">Исключить все позиции</option>
<option value="send-review">Отправить на доработку</option>
</select>
</div>
<div class="kt-ai-field">
<label>Ответственный</label>
<input class="kt-ai-input" name="owner" required placeholder="ФИО">
</div>
<div style="grid-column: 1 / -1; display: flex; gap: var(--kt-ai-space-3);">
<button class="kt-ai-btn" data-variant="primary" type="submit">Сохранить решение</button>
<button class="kt-ai-btn" data-variant="ghost" type="button" id="btn-close-compare">Закрыть</button>
</div>
</form>
</div>
</div>
</section>
<section class="form-section">
<div class="kt-ai-wrap">
<div class="kt-ai-section-head">
<h2>Добавить кабель</h2>
<p>Заполните форму для создания новой записи</p>
<h2>Добавить позицию в план</h2>
<p>Заполните форму для создания новой строки закупочного плана</p>
</div>
<form id="add-cable-form" class="form-grid">
<form id="add-plan-form" class="form-grid">
<div class="kt-ai-field">
<label>Название *</label>
<input class="kt-ai-input" name="name" required placeholder="Например: АВВГ 3х2.5">
<label>Наименование *</label>
<input class="kt-ai-input" name="name" required placeholder="Например: Кабель АВВГ 3х2.5">
</div>
<div class="kt-ai-field">
<label>Тип *</label>
<select class="kt-ai-select" name="type" required>
<option value="">Выберите тип</option>
<option value="Силовой">Силовой</option>
<option value="Оптоволоконный">Оптоволоконный</option>
<option value="Коаксиальный">Коаксиальный</option>
<option value="Контрольный">Контрольный</option>
<label>Категория *</label>
<select class="kt-ai-select" name="category" required>
<option value="">Выберите категорию</option>
<option value="Кабель силовой">Кабель силовой</option>
<option value="Кабель оптоволоконный">Кабель оптоволоконный</option>
<option value="Оборудование">Оборудование</option>
<option value="Материалы">Материалы</option>
</select>
</div>
<div class="kt-ai-field">
<label>Сечение, мм² *</label>
<input class="kt-ai-input" name="section" type="number" step="0.1" required placeholder="2.5">
<label>Количество *</label>
<input class="kt-ai-input" name="quantity" type="number" required placeholder="100">
</div>
<div class="kt-ai-field">
<label>Длина, м *</label>
<input class="kt-ai-input" name="length" type="number" required placeholder="100">
<label>Ед. изм.</label>
<select class="kt-ai-select" name="unit">
<option value="м">метры</option>
<option value="шт">штуки</option>
<option value="кг">килограммы</option>
<option value="комп">комплекты</option>
</select>
</div>
<div class="kt-ai-field">
<label>Статус *</label>
<select class="kt-ai-select" name="status" required>
<label>Цена за ед., ₸ *</label>
<input class="kt-ai-input" name="price" type="number" required placeholder="500">
</div>
<div class="kt-ai-field">
<label>Срок поставки *</label>
<input class="kt-ai-input" name="delivery" type="date" required>
</div>
<div class="kt-ai-field">
<label>Поставщик *</label>
<input class="kt-ai-input" name="supplier" required placeholder="ООО «Пример»">
</div>
<div class="kt-ai-field">
<label>Статус</label>
<select class="kt-ai-select" name="status">
<option value="new">Новый</option>
<option value="in-work">В работе</option>
<option value="ready">Готово</option>
<option value="review">На согласовании</option>
</select>
</div>
<div class="kt-ai-field">
<label>Дата *</label>
<input class="kt-ai-input" name="date" type="date" required>
</div>
<div style="grid-column: 1 / -1;">
<button class="kt-ai-btn" data-variant="primary" type="submit">Добавить в учёт</button>
<button class="kt-ai-btn" data-variant="primary" type="submit">Добавить в план</button>
</div>
</form>
</div>
</section>
<div class="kt-ai-wrap kt-ai-pagefoot">
<span>© 2026 Казахтелеком — Учёт кабельной продукции</span>
<span>© 2026 Казахтелеком — Сравнение закупочного плана</span>
<span class="kt-ai-spacer"></span>
<span>Демо-прототип</span>
</div>
<script>
const STORAGE_KEY = 'kt_cables_data';
const STORAGE_KEY = 'kt_procurement_data';
const DEFAULT_CABLES = [
{ id: 1, name: 'АВВГ 3х2.5', type: 'Силовой', section: 2.5, length: 150, status: 'in-work', date: '2026-09-10' },
{ id: 2, name: 'АВВГ 3х4.0', type: 'Силовой', section: 4.0, length: 200, status: 'new', date: '2026-09-12' },
{ id: 3, name: 'АВВГ 3х2.5', type: 'Силовой', section: 2.5, length: 80, status: 'ready', date: '2026-09-08' },
{ id: 4, name: 'ОКЛ-Т 4х9', type: 'Оптоволоконный', section: 9, length: 500, status: 'in-work', date: '2026-09-11' },
{ id: 5, name: 'КВВГ 7х1.5', type: 'Контрольный', section: 1.5, length: 120, status: 'archived', date: '2026-09-05' }
const DEFAULT_PLAN = [
{ id: 1, name: 'Кабель АВВГ 3х2.5', category: 'Кабель силовой', quantity: 150, unit: 'м', price: 450, sum: 67500, delivery: '2026-09-20', supplier: 'ТОО «КабельКЗ»', status: 'new', period: '2026-09' },
{ id: 2, name: 'Кабель АВВГ 3х2.5', category: 'Кабель силовой', quantity: 200, unit: 'м', price: 420, sum: 84000, delivery: '2026-09-25', supplier: 'ИП «Алмаз»', status: 'review', period: '2026-09' },
{ id: 3, name: 'Кабель АВВГ 3х2.5', category: 'Кабель силовой', quantity: 100, unit: 'м', price: 480, sum: 48000, delivery: '2026-09-15', supplier: 'ТОО «ЭлектроМир»', status: 'new', period: '2026-09' },
{ id: 4, name: 'Кабель АВВГ 3х4.0', category: 'Кабель силовой', quantity: 180, unit: 'м', price: 680, sum: 122400, delivery: '2026-09-22', supplier: 'ТОО «КабельКЗ»', status: 'new', period: '2026-09' },
{ id: 5, name: 'Кабель ОКЛ-Т 4х9', category: 'Кабель оптоволоконный', quantity: 500, unit: 'м', price: 1250, sum: 625000, delivery: '2026-10-10', supplier: 'ТОО «Оптима»', status: 'review', period: '2026-10' },
{ id: 6, name: 'Кабель ОКЛ-Т 4х9', category: 'Кабель оптоволоконный', quantity: 450, unit: 'м', price: 1180, sum: 531000, delivery: '2026-10-05', supplier: 'АО «ТелекомСнаб»', status: 'new', period: '2026-10' },
{ id: 7, name: 'Коммутатор Cisco 2960', category: 'Оборудование', quantity: 5, unit: 'шт', price: 185000, sum: 925000, delivery: '2026-10-15', supplier: 'ТОО «IT-Дистрибьюция»', status: 'approved', period: '2026-10' },
{ id: 8, name: 'Кронштейн универсальный', category: 'Материалы', quantity: 50, unit: 'шт', price: 2500, sum: 125000, delivery: '2026-09-18', supplier: 'ИП «СтройМетиз»', status: 'new', period: '2026-09' },
{ id: 9, name: 'Кронштейн универсальный', category: 'Материалы', quantity: 60, unit: 'шт', price: 2300, sum: 138000, delivery: '2026-09-28', supplier: 'ТОО «КрепежЦентр»', status: 'review', period: '2026-09' }
];
function getCables() {
let selectedIds = [];
let excludedIds = [];
let decisions = [];
function getData() {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
const parsed = JSON.parse(stored);
return parsed.userCables || [];
return parsed.planItems || DEFAULT_PLAN;
}
return DEFAULT_CABLES;
return DEFAULT_PLAN;
}
function saveCable(cable) {
function savePlanItem(item) {
const stored = localStorage.getItem(STORAGE_KEY);
let data = stored ? JSON.parse(stored) : { userCables: [] };
if (!data.userCables) data.userCables = [];
cable.id = Date.now();
data.userCables.push(cable);
let data = stored ? JSON.parse(stored) : { planItems: [], decisions: [] };
if (!data.planItems) data.planItems = [];
item.id = Date.now();
item.sum = (item.quantity * item.price);
data.planItems.push(item);
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
}
function deleteCable(id) {
function saveDecision(decision) {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
let data = JSON.parse(stored);
if (data.userCables) {
data.userCables = data.userCables.filter(c => c.id !== id);
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
}
}
let data = stored ? JSON.parse(stored) : { planItems: [], decisions: [] };
if (!data.decisions) data.decisions = [];
decision.id = Date.now();
decision.date = new Date().toISOString();
data.decisions.push(decision);
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
decisions = data.decisions;
}
function getStatusChip(status) {
const map = {
'new': { label: 'Новый', tone: 'blue' },
'in-work': { label: 'В работе', tone: 'orange' },
'ready': { label: 'Готово', tone: 'green' },
'archived': { label: 'Архив', tone: 'gray' }
'review': { label: 'На согласовании', tone: 'orange' },
'approved': { label: 'Утверждено', tone: 'green' },
'rejected': { label: 'Отклонено', tone: 'red' }
};
const s = map[status] || { label: status, tone: 'gray' };
return `<span class="kt-ai-chip" data-tone="${s.tone}">${s.label}</span>`;
@ -236,85 +329,239 @@ function formatDate(dateStr) {
return d.toLocaleDateString('ru-RU', { day: '2-digit', month: '2-digit', year: '2-digit' });
}
function updateKPI(cables) {
document.getElementById('kpi-total').textContent = cables.length;
document.getElementById('kpi-in-work').textContent = cables.filter(c => c.status === 'in-work').length;
document.getElementById('kpi-ready').textContent = cables.filter(c => c.status === 'ready').length;
document.getElementById('kpi-meters').textContent = cables.reduce((sum, c) => sum + (parseInt(c.length) || 0), 0);
function formatMoney(amount) {
return new Intl.NumberFormat('ru-RU').format(amount);
}
function renderTable(cables) {
const tbody = document.getElementById('cable-table-body');
function updateKPI(items) {
document.getElementById('kpi-total').textContent = items.length;
const groups = new Set(items.map(i => i.name)).size;
document.getElementById('kpi-groups').textContent = groups;
document.getElementById('kpi-excluded').textContent = excludedIds.length;
document.getElementById('kpi-sum').textContent = formatMoney(items.reduce((sum, i) => sum + (i.sum || 0), 0));
}
function renderTable(items) {
const tbody = document.getElementById('plan-table-body');
const empty = document.getElementById('empty-state');
if (cables.length === 0) {
if (items.length === 0) {
tbody.innerHTML = '';
empty.style.display = 'block';
return;
}
empty.style.display = 'none';
tbody.innerHTML = cables.map(c => `
<tr>
<td>${c.name}</td>
<td>${c.type}</td>
<td class="num">${c.section}</td>
<td class="num">${c.length}</td>
<td>${getStatusChip(c.status)}</td>
<td>${formatDate(c.date)}</td>
tbody.innerHTML = items.map(item => {
const isSelected = selectedIds.includes(item.id);
const isExcluded = excludedIds.includes(item.id);
const rowClass = [
isSelected ? 'selected' : '',
isExcluded ? 'excluded' : ''
].filter(Boolean).join(' ') || '';
return `
<tr class="${rowClass}" data-id="${item.id}">
<td>
<input type="checkbox" ${isSelected ? 'checked' : ''} onchange="toggleSelect(${item.id})">
</td>
<td>${item.name}</td>
<td>${item.category}</td>
<td class="num">${item.quantity}</td>
<td class="num">${item.unit}</td>
<td class="num">${formatMoney(item.price)}</td>
<td class="num">${formatMoney(item.sum)}</td>
<td>${formatDate(item.delivery)}</td>
<td>${item.supplier}</td>
<td>${getStatusChip(item.status)}</td>
<td class="actions">
<button class="kt-ai-btn" data-variant="ghost" data-size="sm" onclick="deleteRow(${c.id})">Удалить</button>
<button class="kt-ai-btn" data-variant="ghost" data-size="sm" onclick="excludeItem(${item.id})" ${isExcluded ? 'disabled' : ''}>
${isExcluded ? 'Исключено' : 'Исключить'}
</button>
</td>
</tr>
`).join('');
`}).join('');
const compareBtn = document.getElementById('btn-compare');
compareBtn.disabled = selectedIds.length < 2;
compareBtn.textContent = selectedIds.length > 0
? `Сравнить выбранные (${selectedIds.length})`
: 'Сравнить выбранные';
}
window.toggleSelect = function(id) {
const idx = selectedIds.indexOf(id);
if (idx > -1) {
selectedIds.splice(idx, 1);
} else {
if (selectedIds.length >= 4) {
alert('Можно выбрать не более 4 позиций для сравнения');
return;
}
selectedIds.push(id);
}
applyFilters();
};
window.excludeItem = function(id) {
const reason = prompt('Укажите причину исключения:');
if (reason) {
excludedIds.push(id);
applyFilters();
}
};
function applyFilters() {
const typeFilter = document.getElementById('filter-type').value;
const categoryFilter = document.getElementById('filter-category').value;
const statusFilter = document.getElementById('filter-status').value;
const searchFilter = document.getElementById('filter-search').value.toLowerCase();
const periodFilter = document.getElementById('filter-period').value;
let cables = getCables();
let items = getData();
if (typeFilter) cables = cables.filter(c => c.type === typeFilter);
if (statusFilter) cables = cables.filter(c => c.status === statusFilter);
if (searchFilter) cables = cables.filter(c =>
c.name.toLowerCase().includes(searchFilter) ||
String(c.section).includes(searchFilter)
);
if (categoryFilter) items = items.filter(i => i.category === categoryFilter);
if (statusFilter) items = items.filter(i => i.status === statusFilter);
if (periodFilter && periodFilter !== 'all') {
if (periodFilter.includes('Q')) {
const quarter = periodFilter.split('-Q')[1];
const year = periodFilter.split('-Q')[0];
const qMonths = { '4': ['10', '11', '12'] }[quarter];
items = items.filter(i => qMonths.some(m => i.period.startsWith(year + '-' + m)));
} else {
items = items.filter(i => i.period === periodFilter);
}
}
updateKPI(cables);
renderTable(cables);
updateKPI(items);
renderTable(items);
}
function deleteRow(id) {
deleteCable(id);
applyFilters();
function showComparePanel() {
if (selectedIds.length < 2) {
alert('Выберите минимум 2 позиции для сравнения');
return;
}
const items = getData().filter(i => selectedIds.includes(i.id));
const panel = document.getElementById('compare-panel');
const cardsContainer = document.getElementById('compare-cards');
cardsContainer.innerHTML = items.map((item, idx) => `
<div class="compare-card">
<h4>${item.name} #${idx + 1}</h4>
<div class="compare-row">
<span class="compare-label">Поставщик</span>
<span class="compare-value">${item.supplier}</span>
</div>
<div class="compare-row">
<span class="compare-label">Цена за ед.</span>
<span class="compare-value">${formatMoney(item.price)} ₸</span>
</div>
<div class="compare-row">
<span class="compare-label">Количество</span>
<span class="compare-value">${item.quantity} ${item.unit}</span>
</div>
<div class="compare-row">
<span class="compare-label">Сумма</span>
<span class="compare-value">${formatMoney(item.sum)} ₸</span>
</div>
<div class="compare-row">
<span class="compare-label">Срок поставки</span>
<span class="compare-value ${idx === 0 ? 'diff-highlight' : ''}">${formatDate(item.delivery)}</span>
</div>
<div class="compare-row">
<span class="compare-label">Статус</span>
<span class="compare-value">${getStatusChip(item.status)}</span>
</div>
<div style="margin-top: var(--kt-ai-space-3);">
<button class="kt-ai-btn" data-variant="ghost" data-size="sm" style="width: 100%;" onclick="excludeFromCompare(${item.id})">
Исключить эту позицию
</button>
</div>
</div>
`).join('');
panel.classList.add('active');
panel.scrollIntoView({ behavior: 'smooth' });
}
document.getElementById('filter-type').addEventListener('change', applyFilters);
document.getElementById('filter-status').addEventListener('change', applyFilters);
document.getElementById('filter-search').addEventListener('input', applyFilters);
window.excludeFromCompare = function(id) {
const reason = prompt('Укажите причину исключения:');
if (reason) {
excludedIds.push(id);
selectedIds = selectedIds.filter(sid => sid !== id);
applyFilters();
document.getElementById('compare-panel').classList.remove('active');
}
};
document.getElementById('btn-reset').addEventListener('click', () => {
document.getElementById('filter-type').value = '';
document.getElementById('filter-status').value = '';
document.getElementById('filter-search').value = '';
document.getElementById('btn-compare').addEventListener('click', showComparePanel);
document.getElementById('btn-close-compare').addEventListener('click', () => {
document.getElementById('compare-panel').classList.remove('active');
});
document.getElementById('decision-form').addEventListener('submit', (e) => {
e.preventDefault();
const form = e.target;
const decision = {
selectedIds: [...selectedIds],
excludedIds: [...excludedIds],
comment: form.comment.value,
decision: form.decision.value,
owner: form.owner.value
};
saveDecision(decision);
alert('Решение сохранено!');
form.reset();
document.getElementById('compare-panel').classList.remove('active');
selectedIds = [];
applyFilters();
});
document.getElementById('add-cable-form').addEventListener('submit', (e) => {
document.getElementById('btn-export').addEventListener('click', () => {
const items = getData();
const csvContent = [
['ID', 'Наименование', 'Категория', 'Количество', 'Ед. изм.', 'Цена', 'Сумма', 'Срок поставки', 'Поставщик', 'Статус', 'Исключено'],
...items.map(i => [
i.id, i.name, i.category, i.quantity, i.unit, i.price, i.sum, i.delivery, i.supplier, i.status,
excludedIds.includes(i.id) ? 'Да' : 'Нет'
])
].map(row => row.join(';')).join('\n');
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = 'закупочный_план_' + new Date().toISOString().slice(0, 10) + '.csv';
link.click();
});
document.getElementById('filter-category').addEventListener('change', applyFilters);
document.getElementById('filter-status').addEventListener('change', applyFilters);
document.getElementById('filter-period').addEventListener('change', applyFilters);
document.getElementById('btn-reset').addEventListener('click', () => {
document.getElementById('filter-category').value = '';
document.getElementById('filter-status').value = '';
document.getElementById('filter-period').value = 'all';
selectedIds = [];
applyFilters();
});
document.getElementById('add-plan-form').addEventListener('submit', (e) => {
e.preventDefault();
const form = e.target;
const cable = {
const item = {
name: form.name.value,
type: form.type.value,
section: parseFloat(form.section.value),
length: parseInt(form.length.value),
category: form.category.value,
quantity: parseInt(form.quantity.value),
unit: form.unit.value,
price: parseInt(form.price.value),
delivery: form.delivery.value,
supplier: form.supplier.value,
status: form.status.value,
date: form.date.value
period: form.delivery.value.slice(0, 7)
};
saveCable(cable);
savePlanItem(item);
form.reset();
applyFilters();
});