// Хранилище данных (localStorage + экспорт/импорт) const DB_VERSION = 1; const defaultData = { employees: [], decrees: [], payments: [], currentPeriod: '2026-08', operations: [] }; let db = loadData(); function loadData() { try { const saved = localStorage.getItem('executive_sheets_db'); if (saved) { const parsed = JSON.parse(saved); return { ...defaultData, ...parsed }; } } catch (e) { console.error('Ошибка загрузки данных:', e); } return { ...defaultData }; } function saveData() { try { localStorage.setItem('executive_sheets_db', JSON.stringify(db)); } catch (e) { console.error('Ошибка сохранения данных:', e); showToast('Ошибка сохранения данных', 'error'); } } function addEmployee(emp) { const existing = db.employees.find(e => e.tabNum === emp.tabNum); if (existing) { existing.name = emp.name; existing.iin = emp.iin; existing.salary = parseFloat(emp.salary) || 0; existing.updatedAt = new Date().toISOString(); } else { db.employees.push({ ...emp, id: Date.now(), salary: parseFloat(emp.salary) || 0, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }); } saveData(); logOperation('employee', 'create', `Сотрудник ${emp.name} (таб. ${emp.tabNum})`); } function updateEmployee(id, updates) { const emp = db.employees.find(e => e.id === id); if (emp) { Object.assign(emp, updates); emp.updatedAt = new Date().toISOString(); saveData(); logOperation('employee', 'update', `Сотрудник ${emp.name}`); } } function deleteEmployee(id) { const idx = db.employees.findIndex(e => e.id === id); if (idx !== -1) { const name = db.employees[idx].name; db.employees.splice(idx, 1); saveData(); logOperation('employee', 'delete', `Сотрудник ${name}`); } } function addDecree(decree) { decree.id = Date.now(); decree.status = 'active'; decree.paidAmount = 0; decree.createdAt = new Date().toISOString(); db.decrees.push(decree); saveData(); logOperation('decree', 'create', `Постановление ${decree.number}`); } function updateDecree(id, updates) { const d = db.decrees.find(d => d.id === id); if (d) { Object.assign(d, updates); d.updatedAt = new Date().toISOString(); saveData(); logOperation('decree', 'update', `Постановление ${d.number}`); } } function deleteDecree(id) { const idx = db.decrees.findIndex(d => d.id === id); if (idx !== -1) { const num = db.decrees[idx].number; db.decrees.splice(idx, 1); saveData(); logOperation('decree', 'delete', `Постановление ${num}`); } } function addPayment(payment) { payment.id = Date.now(); payment.status = 'processed'; payment.createdAt = new Date().toISOString(); db.payments.push(payment); const decree = db.decrees.find(d => d.id === payment.decreeId); if (decree) { decree.paidAmount = (decree.paidAmount || 0) + parseFloat(payment.amount); if (decree.paidAmount >= decree.amount) { decree.status = 'closed'; } saveData(); } logOperation('payment', 'create', `Выплата по ${payment.decreeNumber}`); } function getEmployees() { return db.employees; } function getDecrees(filter = 'all') { if (filter === 'all') return db.decrees; if (filter === 'alimony') return db.decrees.filter(d => d.type === 'alimony'); if (filter === 'other') return db.decrees.filter(d => d.type === 'other'); if (filter === 'closed') return db.decrees.filter(d => d.status === 'closed'); return db.decrees; } function getDecreesForEmployee(empId) { return db.decrees.filter(d => d.employeeId === empId && d.status === 'active'); } function getPayments() { return db.payments; } function getCurrentPeriod() { return db.currentPeriod; } function setCurrentPeriod(period) { db.currentPeriod = period; saveData(); } function logOperation(type, action, description) { db.operations.unshift({ id: Date.now(), type, action, description, timestamp: new Date().toISOString() }); if (db.operations.length > 100) { db.operations = db.operations.slice(0, 100); } saveData(); } function getRecentOperations(limit = 5) { return db.operations.slice(0, limit); } function getStats() { const activeDecrees = db.decrees.filter(d => d.status === 'active'); const totalDebt = activeDecrees.reduce((sum, d) => sum + (d.amount - (d.paidAmount || 0)), 0); const currentPeriod = getCurrentPeriod(); const paidThisMonth = db.payments .filter(p => p.period === currentPeriod) .reduce((sum, p) => sum + parseFloat(p.amount), 0); return { employees: db.employees.length, activeDecrees: activeDecrees.length, totalDebt, paidThisMonth, closedDecrees: db.decrees.filter(d => d.status === 'closed').length }; } function calculateDeductionsForEmployee(employee) { const decrees = getDecreesForEmployee(employee.id); if (decrees.length === 0) return null; const maxDeduction = employee.salary * 0.5; const alimonyDecrees = decrees.filter(d => d.type === 'alimony'); const otherDecrees = decrees.filter(d => d.type === 'other'); let totalAllocated = 0; const allocation = []; alimonyDecrees.forEach(d => { const remaining = d.amount - (d.paidAmount || 0); allocation.push({ decree: d, allocated: Math.min(remaining, maxDeduction * 0.5) }); totalAllocated += allocation[allocation.length - 1].allocated; }); const remainingForOther = maxDeduction - totalAllocated; if (remainingForOther > 0 && otherDecrees.length > 0) { const perOther = remainingForOther / otherDecrees.length; otherDecrees.forEach(d => { const remaining = d.amount - (d.paidAmount || 0); allocation.push({ decree: d, allocated: Math.min(remaining, perOther) }); }); } return { employee, maxDeduction, allocation, totalAllocated, remaining: employee.salary - maxDeduction }; } function exportToCSV(data, filename) { if (!data || data.length === 0) { showToast('Нет данных для экспорта', 'error'); return; } const headers = Object.keys(data[0]); const csv = [ headers.join(';'), ...data.map(row => headers.map(h => { const val = row[h] !== null && row[h] !== undefined ? String(row[h]) : ''; return '"' + val.replace(/"/g, '""') + '"'; }).join(';') ) ].join('\n'); const blob = new Blob(['\uFEFF' + csv], { type: 'text/csv;charset=utf-8;' }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = filename; link.click(); showToast('Файл скачан: ' + filename, 'success'); } function clearAllData() { if (confirm('Вы уверены? Все данные будут удалены безвозвратно.')) { localStorage.removeItem('executive_sheets_db'); db = { ...defaultData }; showToast('Все данные удалены', 'success'); location.reload(); } }