const STORAGE_KEY = 'uchet_komandirovok_data'; const CURRENT_USER = 'Ответственный за учёт'; const DEMO_DATA = { items: [ { id: 1, employee: 'Айдос Нурланов', city: 'Астана', dateStart: '2026-08-12', dateEnd: '2026-08-15', purpose: 'Участие в международной конференции по цифровизации', amount: 185000, status: 'согласовано' }, { id: 2, employee: 'Гульназ Сейдахметова', city: 'Алматы', dateStart: '2026-09-03', dateEnd: '2026-09-05', purpose: 'Переговоры с партнёрами по проекту', amount: 95000, status: 'на согласовании' }, { id: 3, employee: 'Ержан Тлеубергенов', city: 'Шымкент', dateStart: '2026-09-18', dateEnd: '2026-09-20', purpose: 'Обучение сотрудников филиала', amount: 72000, status: 'на согласовании' }, { id: 4, employee: 'Алия Касымова', city: 'Караганда', dateStart: '2026-10-05', dateEnd: '2026-10-07', purpose: 'Аудит регионального офиса', amount: 68000, status: 'согласовано' }, { id: 5, employee: 'Айдос Нурланов', city: 'Актобе', dateStart: '2026-10-12', dateEnd: '2026-10-14', purpose: 'Презентация нового продукта', amount: 89000, status: 'отклонено' }, { id: 6, employee: 'Гульназ Сейдахметова', city: 'Астана', dateStart: '2026-11-02', dateEnd: '2026-11-06', purpose: 'Участие в выставке EXPO', amount: 145000, status: 'согласовано' }, { id: 7, employee: 'Ержан Тлеубергенов', city: 'Алматы', dateStart: '2026-11-15', dateEnd: '2026-11-17', purpose: 'Встреча с инвесторами', amount: 112000, status: 'на согласовании' } ], log: [ { when: '2026-08-01 10:23', what: 'Создана запись: Айдос Нурланов — Астана', who: 'Ответственный за учёт' }, { when: '2026-08-01 14:15', what: 'Статус изменён на «согласовано»: Айдос Нурланов', who: 'Ответственный за учёт' }, { when: '2026-08-02 09:45', what: 'Создана запись: Гульназ Сейдахметова — Алматы', who: 'Ответственный за учёт' }, { when: '2026-08-02 11:30', what: 'Создана запись: Ержан Тлеубергенов — Шымкент', who: 'Ответственный за учёт' }, { when: '2026-08-03 16:20', what: 'Создана запись: Алия Касымова — Караганда', who: 'Ответственный за учёт' }, { when: '2026-08-04 10:00', what: 'Статус изменён на «согласовано»: Алия Касымова', who: 'Ответственный за учёт' }, { when: '2026-08-05 13:45', what: 'Создана запись: Айдос Нурланов — Актобе', who: 'Ответственный за учёт' }, { when: '2026-08-05 15:30', what: 'Статус изменён на «отклонено»: Айдос Нурланов', who: 'Ответственный за учёт' }, { when: '2026-08-06 09:15', what: 'Создана запись: Гульназ Сейдахметова — Астана', who: 'Ответственный за учёт' }, { when: '2026-08-07 11:00', what: 'Создана запись: Ержан Тлеубергенов — Алматы', who: 'Ответственный за учёт' } ] }; function loadData() { try { const stored = localStorage.getItem(STORAGE_KEY); if (stored) { return JSON.parse(stored); } } catch (e) { console.error('Ошибка загрузки данных:', e); } return JSON.parse(JSON.stringify(DEMO_DATA)); } function saveData(data) { localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); } function formatDate(dateStr) { const [year, month, day] = dateStr.split('-'); return `${day}.${month}.${year}`; } function formatDateTime() { const now = new Date(); const year = now.getFullYear(); const month = String(now.getMonth() + 1).padStart(2, '0'); const day = String(now.getDate()).padStart(2, '0'); const hours = String(now.getHours()).padStart(2, '0'); const minutes = String(now.getMinutes()).padStart(2, '0'); return `${year}-${month}-${day} ${hours}:${minutes}`; } function getStatusClass(status) { if (status === 'согласовано') return 'status-approved'; if (status === 'отклонено') return 'status-rejected'; return 'status-pending'; } function getStatusLabel(status) { if (status === 'согласовано') return '✓ Согласовано'; if (status === 'отклонено') return '✗ Отклонено'; return '⏳ На согласовании'; } let data = loadData(); let currentFilter = ''; let searchQuery = ''; function renderStats() { const total = data.items.length; const pending = data.items.filter(i => i.status === 'на согласовании').length; const approved = data.items.filter(i => i.status === 'согласовано').length; const rejected = data.items.filter(i => i.status === 'отклонено').length; document.getElementById('stat-total').textContent = total; document.getElementById('stat-pending').textContent = pending; document.getElementById('stat-approved').textContent = approved; document.getElementById('stat-rejected').textContent = rejected; } function renderTable() { const tbody = document.getElementById('table-body'); let filtered = data.items; if (currentFilter) { filtered = filtered.filter(i => i.status === currentFilter); } if (searchQuery) { const q = searchQuery.toLowerCase(); filtered = filtered.filter(i => i.employee.toLowerCase().includes(q) || i.city.toLowerCase().includes(q) || i.purpose.toLowerCase().includes(q) ); } if (filtered.length === 0) { tbody.innerHTML = `

Нет записей для отображения

`; return; } tbody.innerHTML = filtered.map(item => ` ${item.employee} ${item.city} ${formatDate(item.dateStart)} – ${formatDate(item.dateEnd)} ${item.purpose} ${item.amount.toLocaleString('ru-RU')} ${getStatusLabel(item.status)}
`).join(''); } function renderLog() { const tbody = document.getElementById('log-body'); if (data.log.length === 0) { tbody.innerHTML = 'Журнал пуст'; return; } const sortedLog = [...data.log].reverse(); tbody.innerHTML = sortedLog.map(entry => ` ${entry.when} ${entry.what} ${entry.who} `).join(''); } function openModal(modalId) { document.getElementById(modalId).classList.add('active'); } function closeModal(modalId) { document.getElementById(modalId).classList.remove('active'); } function openForm(id = null) { const modal = document.getElementById('modal-form'); const title = document.getElementById('modal-title'); const form = document.getElementById('form'); form.reset(); document.getElementById('form-id').value = ''; if (id) { const item = data.items.find(i => i.id === id); if (item) { title.textContent = 'Редактировать командировку'; document.getElementById('form-id').value = item.id; document.getElementById('form-employee').value = item.employee; document.getElementById('form-city').value = item.city; document.getElementById('form-date-start').value = item.dateStart; document.getElementById('form-date-end').value = item.dateEnd; document.getElementById('form-purpose').value = item.purpose; document.getElementById('form-amount').value = item.amount; document.getElementById('form-status').value = item.status; } } else { title.textContent = 'Добавить командировку'; } openModal('modal-form'); } function editItem(id) { openForm(id); } function deleteItem(id) { const item = data.items.find(i => i.id === id); if (!item) return; if (confirm(`Вы уверены, что хотите удалить запись?\n\nСотрудник: ${item.employee}\nГород: ${item.city}\nДаты: ${formatDate(item.dateStart)} – ${formatDate(item.dateEnd)}`)) { const logEntry = { when: formatDateTime(), what: `Удалена запись: ${item.employee} — ${item.city}`, who: CURRENT_USER }; data.items = data.items.filter(i => i.id !== id); data.log.push(logEntry); saveData(data); renderStats(); renderTable(); renderLog(); } } function addItem(item) { const logEntry = { when: formatDateTime(), what: `Создана запись: ${item.employee} — ${item.city}`, who: CURRENT_USER }; data.items.push(item); data.log.push(logEntry); saveData(data); renderStats(); renderTable(); renderLog(); } function updateItem(id, updatedItem) { const oldItem = data.items.find(i => i.id === id); if (!oldItem) return; const changes = []; if (oldItem.employee !== updatedItem.employee) changes.push(`сотрудник: ${oldItem.employee} → ${updatedItem.employee}`); if (oldItem.city !== updatedItem.city) changes.push(`город: ${oldItem.city} → ${updatedItem.city}`); if (oldItem.status !== updatedItem.status) changes.push(`статус: ${oldItem.status} → ${updatedItem.status}`); const logEntry = { when: formatDateTime(), what: `Изменена запись: ${oldItem.employee} — ${updatedItem.city}` + (changes.length > 0 ? ` (${changes.join(', ')})` : ''), who: CURRENT_USER }; const index = data.items.findIndex(i => i.id === id); data.items[index] = { ...updatedItem, id }; data.log.push(logEntry); saveData(data); renderStats(); renderTable(); renderLog(); } function exportCsv() { const headers = ['Сотрудник', 'Город', 'Дата начала', 'Дата окончания', 'Цель', 'Сумма (тенге)', 'Статус']; const rows = data.items.map(item => [ item.employee, item.city, formatDate(item.dateStart), formatDate(item.dateEnd), item.purpose, item.amount.toString(), item.status ]); const csvContent = '\uFEFF' + [ headers.join(';'), ...rows.map(row => row.map(cell => '"' + String(cell).replace(/"/g, '""') + '"').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 = 'komandirovki_' + new Date().toISOString().slice(0, 10) + '.csv'; link.click(); URL.revokeObjectURL(link.href); } document.addEventListener('DOMContentLoaded', () => { renderStats(); renderTable(); renderLog(); document.getElementById('btn-add').addEventListener('click', () => openForm()); document.getElementById('close-form').addEventListener('click', () => closeModal('modal-form')); document.getElementById('cancel-form').addEventListener('click', () => closeModal('modal-form')); document.getElementById('modal-form').addEventListener('click', (e) => { if (e.target.id === 'modal-form') closeModal('modal-form'); }); document.getElementById('btn-log').addEventListener('click', () => { renderLog(); openModal('modal-log'); }); document.getElementById('close-log').addEventListener('click', () => closeModal('modal-log')); document.getElementById('modal-log').addEventListener('click', (e) => { if (e.target.id === 'modal-log') closeModal('modal-log'); }); document.getElementById('btn-export').addEventListener('click', exportCsv); document.getElementById('search').addEventListener('input', (e) => { searchQuery = e.target.value; renderTable(); }); document.querySelectorAll('.chip').forEach(chip => { chip.addEventListener('click', () => { document.querySelectorAll('.chip').forEach(c => c.classList.remove('active')); chip.classList.add('active'); currentFilter = chip.dataset.status; renderTable(); }); }); document.getElementById('form').addEventListener('submit', (e) => { e.preventDefault(); const id = document.getElementById('form-id').value; const item = { employee: document.getElementById('form-employee').value, city: document.getElementById('form-city').value, dateStart: document.getElementById('form-date-start').value, dateEnd: document.getElementById('form-date-end').value, purpose: document.getElementById('form-purpose').value, amount: parseInt(document.getElementById('form-amount').value, 10), status: document.getElementById('form-status').value }; if (id) { updateItem(parseInt(id, 10), item); } else { item.id = Date.now(); addItem(item); } closeModal('modal-form'); }); });