// Глобальные переменные let currentUser = null; let editingReportId = null; let editingUserId = null; let editingStoreId = null; let editingTodoId = null; // База данных (симуляция) let database = { users: [ { id: 1, username: "admin", password: "admin123", role: "admin", stores: [], }, { id: 2, username: "employee", password: "password123", role: "employee", stores: [1, 2], }, { id: 3, username: "cashier1", password: "password123", role: "employee", stores: [1, 2], }, { id: 4, username: "cashier2", password: "password123", role: "employee", stores: [3], }, ], stores: [ { id: 1, name: "Магазин 1" }, { id: 2, name: "Магазин 2" }, { id: 3, name: "Магазин 3" }, { id: 4, name: "Магазин 4" }, ], reports: [], todos: [ { id: 1, title: "Исправить модальные окна", description: "Исправить прокрутку и видимость кнопок в модальных окнах", completed: true, priority: "high", createdAt: "2024-01-15", }, { id: 2, title: "Добавить экспорт в PDF", description: "Реализовать функцию экспорта отчетов в PDF формат", completed: false, priority: "medium", createdAt: "2024-01-16", }, { id: 3, title: "Улучшить графики", description: "Добавить больше интерактивности в графики Dashboard", completed: false, priority: "low", createdAt: "2024-01-17", }, ], }; // Генерация тестовых данных для отчетов function generateTestData() { const reports = []; const today = new Date(); for (let i = 0; i < 30; i++) { const date = new Date(today); date.setDate(date.getDate() - i); const storeId = Math.floor(Math.random() * 4) + 1; const userId = Math.floor(Math.random() * 3) + 2; // employee пользователи const income = Math.floor(Math.random() * 2000) + 500; const cajaInicial = Math.floor(Math.random() * 300) + 100; const totalIncome = income + cajaInicial; const wages = Math.floor(Math.random() * 400) + 100; const expenses = Math.floor(Math.random() * 200) + 50; const totalExpenses = wages + expenses; const envelope = Math.floor(Math.random() * 200) + 100; const cajaFinal = totalIncome - totalExpenses - envelope; reports.push({ id: i + 1, date: date.toISOString().split("T")[0], storeId: storeId, userId: userId, income: income, cajaInicial: cajaInicial, totalIncome: totalIncome, wages: [ { name: "Сотрудник " + (Math.floor(Math.random() * 3) + 1), amount: wages, }, ], expenses: [ { name: "Расходы " + (Math.floor(Math.random() * 3) + 1), amount: expenses, }, ], totalWages: wages, totalExpensesInternal: expenses, totalExpenses: totalExpenses, envelope: envelope, cajaFinal: cajaFinal, verified: Math.random() > 0.3, createdAt: date.toISOString(), }); } database.reports = reports; } // Инициализация тестовых данных generateTestData(); // Система уведомлений function showNotification(message, type = "success") { const notification = document.getElementById("notification"); const notificationText = document.getElementById("notificationText"); if (!notification || !notificationText) return; notificationText.textContent = message; notification.className = `fixed top-4 right-4 z-50 animate-fade-in`; if (type === "error") { notification.innerHTML = `
${message}
`; } else { notification.innerHTML = `
${message}
`; } notification.classList.remove("hidden"); setTimeout(() => { notification.classList.add("hidden"); }, 3000); } // Управление модальными окнами с исправленной прокруткой function showModal(modalId) { const modal = document.getElementById(modalId); if (modal) { modal.classList.add("show"); // Блокируем прокрутку body document.body.classList.add("modal-open"); } } function hideModal(modalId) { const modal = document.getElementById(modalId); if (modal) { modal.classList.remove("show"); // Разблокируем прокрутку body document.body.classList.remove("modal-open"); } } // Закрытие модального окна при клике вне его document.addEventListener("click", (e) => { if (e.target.classList.contains("modal")) { hideModal(e.target.id); } }); // Обработчики авторизации // document.getElementById("loginForm").addEventListener("submit", (e) => { // e.preventDefault(); // const username = document.getElementById("username").value; // const password = document.getElementById("password").value; // const user = database.users.find( // (u) => u.username === username && u.password === password // ); // if (user) { // currentUser = user; // document.getElementById("loginScreen").classList.add("hidden"); // if (user.role === "admin") { // showAdminInterface(); // } else { // showUserInterface(); // } // showNotification("Успешная авторизация!"); // } else { // const errorDiv = document.getElementById("loginError"); // errorDiv.textContent = "Неверный логин или пароль"; // errorDiv.classList.remove("hidden"); // } // }); // Показать интерфейс пользователя function showUserInterface() { document.getElementById("userInterface").classList.remove("hidden"); document.getElementById( "userWelcome" ).textContent = `Добро пожаловать, ${currentUser.username}!`; loadUserStores(); setupFormCalculations(); } // Показать интерфейс администратора function showAdminInterface() { document.getElementById("adminInterface").classList.remove("hidden"); document.getElementById( "adminWelcome" ).textContent = `Добро пожаловать, ${currentUser.username}!`; updateDashboard(); loadReports(); loadUsers(); loadStores(); loadTodos(); setupAdminTabs(); } // Загрузка магазинов для пользователя function loadUserStores() { const select = document.getElementById("storeSelect"); select.innerHTML = ''; let userStores = []; if (currentUser.role === "admin") { userStores = database.stores; } else { userStores = database.stores.filter((store) => currentUser.stores.includes(store.id) ); } userStores.forEach((store) => { const option = document.createElement("option"); option.value = store.id; option.textContent = store.name; select.appendChild(option); }); } // Настройка автоматических расчетов в форме function setupFormCalculations() { const incomeInput = document.getElementById("income"); const cajaInicialInput = document.getElementById("cajaInicial"); const envelopeInput = document.getElementById("envelope"); function updateCalculations() { const income = parseFloat(incomeInput.value) || 0; const cajaInicial = parseFloat(cajaInicialInput.value) || 0; const envelope = parseFloat(envelopeInput.value) || 0; const totalIncome = income + cajaInicial; document.getElementById("totalIncome").value = totalIncome.toFixed(2); document.getElementById("displayTotalIncome").value = totalIncome.toFixed(2); const totalWages = calculateTotalWages(); const totalExpensesInternal = calculateTotalExpenses(); const totalExpenses = totalWages + totalExpensesInternal; document.getElementById("totalExpenses").value = totalExpenses.toFixed(2); const cajaFinal = totalIncome - totalExpenses - envelope; document.getElementById("cajaFinal").textContent = cajaFinal.toFixed(2); } incomeInput.addEventListener("input", updateCalculations); cajaInicialInput.addEventListener("input", updateCalculations); envelopeInput.addEventListener("input", updateCalculations); setupDynamicRows(); } // Настройка динамических строк function setupDynamicRows() { document.getElementById("addWage").addEventListener("click", () => { addWageRow(); }); document.getElementById("addExpense").addEventListener("click", () => { addExpenseRow(); }); // Обновление при изменении значений document.addEventListener("input", (e) => { if ( e.target.classList.contains("wage-amount") || e.target.classList.contains("expense-amount") ) { updateTotals(); } }); // Удаление строк document.addEventListener("click", (e) => { if (e.target.classList.contains("remove-wage")) { e.target.closest(".wage-row").remove(); updateTotals(); } else if (e.target.classList.contains("remove-expense")) { e.target.closest(".expense-row").remove(); updateTotals(); } }); } function addWageRow() { const container = document.getElementById("wagesContainer"); const row = document.createElement("div"); row.className = "wage-row grid grid-cols-1 md:grid-cols-3 gap-4 mb-3"; row.innerHTML = ` `; container.appendChild(row); } function addExpenseRow() { const container = document.getElementById("expensesContainer"); const row = document.createElement("div"); row.className = "expense-row grid grid-cols-1 md:grid-cols-3 gap-4 mb-3"; row.innerHTML = ` `; container.appendChild(row); } function calculateTotalWages() { const amounts = document.querySelectorAll(".wage-amount"); let total = 0; amounts.forEach((amount) => { total += parseFloat(amount.value) || 0; }); return total; } function calculateTotalExpenses() { const amounts = document.querySelectorAll(".expense-amount"); let total = 0; amounts.forEach((amount) => { total += parseFloat(amount.value) || 0; }); return total; } function updateTotals() { const totalWages = calculateTotalWages(); const totalExpensesInternal = calculateTotalExpenses(); document.getElementById("totalWages").textContent = totalWages.toFixed(2); document.getElementById("totalExpensesInternal").textContent = totalExpensesInternal.toFixed(2); const totalExpenses = totalWages + totalExpensesInternal; document.getElementById("totalExpenses").value = totalExpenses.toFixed(2); // Обновить Caja Final const income = parseFloat(document.getElementById("income").value) || 0; const cajaInicial = parseFloat(document.getElementById("cajaInicial").value) || 0; const envelope = parseFloat(document.getElementById("envelope").value) || 0; const totalIncome = income + cajaInicial; const cajaFinal = totalIncome - totalExpenses - envelope; document.getElementById("cajaFinal").textContent = cajaFinal.toFixed(2); } // Отправка формы отчета document.getElementById("reportForm").addEventListener("submit", (e) => { e.preventDefault(); const formData = { id: editingReportId || Date.now(), date: new Date().toISOString().split("T")[0], storeId: parseInt(document.getElementById("storeSelect").value), userId: currentUser.id, income: parseFloat(document.getElementById("income").value), cajaInicial: parseFloat(document.getElementById("cajaInicial").value), totalIncome: parseFloat(document.getElementById("totalIncome").value), envelope: parseFloat(document.getElementById("envelope").value), verified: false, createdAt: new Date().toISOString(), }; // Сбор зарплат const wages = []; const wageRows = document.querySelectorAll(".wage-row"); wageRows.forEach((row) => { const name = row.querySelector(".wage-name").value; const amount = parseFloat(row.querySelector(".wage-amount").value) || 0; if (name && amount > 0) { wages.push({ name, amount }); } }); // Сбор расходов const expenses = []; const expenseRows = document.querySelectorAll(".expense-row"); expenseRows.forEach((row) => { const name = row.querySelector(".expense-name").value; const amount = parseFloat(row.querySelector(".expense-amount").value) || 0; if (name && amount > 0) { expenses.push({ name, amount }); } }); formData.wages = wages; formData.expenses = expenses; formData.totalWages = calculateTotalWages(); formData.totalExpensesInternal = calculateTotalExpenses(); formData.totalExpenses = formData.totalWages + formData.totalExpensesInternal; formData.cajaFinal = formData.totalIncome - formData.totalExpenses - formData.envelope; if (editingReportId) { const index = database.reports.findIndex((r) => r.id === editingReportId); database.reports[index] = formData; editingReportId = null; showNotification("Отчет успешно обновлен!"); } else { database.reports.push(formData); showNotification("Отчет успешно сохранен!"); } // Очистка формы document.getElementById("reportForm").reset(); document.getElementById("wagesContainer").innerHTML = `
`; document.getElementById("expensesContainer").innerHTML = `
`; updateTotals(); }); // Отчет за сегодня для пользователя document.getElementById("todayReportBtn").addEventListener("click", () => { const today = new Date().toISOString().split("T")[0]; const todayReport = database.reports.find( (r) => r.date === today && r.userId === currentUser.id ); if (todayReport) { showReportModal(todayReport, false); // false = не админ режим } else { showNotification("Отчет за сегодня не найден", "error"); } }); function safeToFixed(value, digits = 2) { return (Number(value) || 0).toFixed(digits); } // Показ модального окна отчета с исправленной прокруткой function showReportModal(report, isAdmin = false) { const modal = document.getElementById("reportViewModal"); const content = document.getElementById("reportViewContent"); const buttons = document.getElementById("reportModalButtons"); const title = document.getElementById("reportModalTitle"); const store = database.stores.find((s) => s.id === report.storeId); const user = database.users.find((u) => u.id === report.userId); title.textContent = `Отчет от ${report.reportDate || report.date} - ${ report.storeName || report.storeId || "Неизвестный магазин" }`; content.innerHTML = `

Основная информация

Дата: ${ report.date || report.reportDate || "" }
Магазин: ${ report.storeName || report.storeId }
Пользователь: ${ user ? user.username : report.userId }
Статус: ${ report.isVerified || report.verified ? "Проверен" : "Не проверен" }

Доходы (Ingresos)

Income: €${safeToFixed(report.income)}
Caja inicial: €${safeToFixed( report.cajaInicial || report.initialCash )}
Total income: €${safeToFixed( report.totalIncome )}

Зарплаты (Wages)

${ Array.isArray(report.wages) && report.wages.length > 0 ? `
${report.wages .map( (w) => `
${w.name} €${safeToFixed(w.amount)}
` ) .join("")}
Total wages: €${safeToFixed(report.totalWages)}
` : '

Нет данных о зарплатах

' }

Расходы (Expenses)

${ Array.isArray(report.expenses) && report.expenses.length > 0 ? `
${report.expenses .map( (e) => `
${e.name} €${safeToFixed(e.amount)}
` ) .join("")}
Total expenses internal: €${safeToFixed(report.totalExpensesInternal)}
` : '

Нет данных о расходах

' }

Итоговые расчеты

Total income: €${safeToFixed( report.totalIncome )}
Total expenses: €${safeToFixed( report.totalExpenses )}
Envelope: €${safeToFixed( report.envelope )}
Caja final: €${safeToFixed( report.cajaFinal || report.finalCash )}
`; buttons.innerHTML = ""; if (isAdmin) { buttons.innerHTML = ` `; document.getElementById("editReportBtn").addEventListener("click", () => { editReport(report); }); document.getElementById("verifyReportBtn").addEventListener("click", () => { verifyReport(report.id); }); } else { if (!report.isVerified && !report.verified) { buttons.innerHTML = ` `; document .getElementById("editReportUserBtn") .addEventListener("click", () => { fillFormWithReport(report); hideModal("reportViewModal"); }); } else { buttons.innerHTML = ` `; } } document .getElementById("closeReportModalBtn") .addEventListener("click", () => { hideModal("reportViewModal"); }); showModal("reportViewModal"); } // Редактирование отчета администратором // function editReport(report) { // const content = document.getElementById("reportViewContent"); // const buttons = document.getElementById("reportModalButtons"); // // Преобразуем просмотр в форму редактирования // content.innerHTML = ` //
// //
//

Основная информация

//
//
// // //
//
// // //
//
//
// //
//

Доходы

//
//
// // //
//
// // //
//
// // //
//
//
// //
//

Статус

// //
//
// `; // // Изменяем кнопки на режим редактирования // buttons.innerHTML = ` // // // `; // document.getElementById("saveReportBtn").addEventListener("click", () => { // saveEditedReport(report.id); // }); // document.getElementById("cancelEditBtn").addEventListener("click", () => { // showReportModal(report, true); // Вернуться к просмотру // }); // } // Сохранение отредактированного отчета // function saveEditedReport(reportId) { // const reportIndex = database.reports.findIndex((r) => r.id === reportId); // if (reportIndex === -1) return; // const report = database.reports[reportIndex]; // // Обновляем данные // report.storeId = parseInt(document.getElementById("editStoreSelect").value); // report.date = document.getElementById("editDate").value; // report.income = parseFloat(document.getElementById("editIncome").value); // report.cajaInicial = parseFloat( // document.getElementById("editCajaInicial").value // ); // report.envelope = parseFloat(document.getElementById("editEnvelope").value); // report.verified = document.getElementById("editVerified").checked; // // Пересчитываем итоги // report.totalIncome = report.income + report.cajaInicial; // report.cajaFinal = // report.totalIncome - report.totalExpenses - report.envelope; // database.reports[reportIndex] = report; // showNotification("Отчет успешно обновлен!"); // hideModal("reportViewModal"); // // Обновляем таблицу отчетов если мы в админке // if (currentUser.role === "admin") { // loadReports(); // updateDashboard(); // } // } // Подтверждение отчета // function verifyReport(reportId) { // const reportIndex = database.reports.findIndex((r) => r.id === reportId); // if (reportIndex !== -1) { // database.reports[reportIndex].verified = true; // showNotification("Отчет подтвержден!"); // hideModal("reportViewModal"); // loadReports(); // updateDashboard(); // } // } // Заполнение формы данными отчета (для пользователя) function fillFormWithReport(report) { editingReportId = report.id; document.getElementById("storeSelect").value = report.storeId; document.getElementById("income").value = report.income; document.getElementById("cajaInicial").value = report.cajaInicial; document.getElementById("envelope").value = report.envelope; // Заполнение зарплат const wagesContainer = document.getElementById("wagesContainer"); wagesContainer.innerHTML = ""; if (report.wages && report.wages.length > 0) { report.wages.forEach((wage) => { const row = document.createElement("div"); row.className = "wage-row grid grid-cols-1 md:grid-cols-3 gap-4 mb-3"; row.innerHTML = ` `; wagesContainer.appendChild(row); }); } else { addWageRow(); } // Заполнение расходов const expensesContainer = document.getElementById("expensesContainer"); expensesContainer.innerHTML = ""; if (report.expenses && report.expenses.length > 0) { report.expenses.forEach((expense) => { const row = document.createElement("div"); row.className = "expense-row grid grid-cols-1 md:grid-cols-3 gap-4 mb-3"; row.innerHTML = ` `; expensesContainer.appendChild(row); }); } else { addExpenseRow(); } updateTotals(); } // === АДМИН ПАНЕЛЬ === // Настройка вкладок администратора function setupAdminTabs() { const tabButtons = document.querySelectorAll(".admin-tab-btn"); const tabContents = document.querySelectorAll(".admin-tab-content"); tabButtons.forEach((button) => { button.addEventListener("click", () => { const tabId = button.dataset.tab; // Переключение активной вкладки tabButtons.forEach((btn) => { btn.className = "admin-tab-btn px-6 py-3 font-medium text-gray-600 hover:text-blue-600 transition-colors border-b-2 border-transparent hover:border-blue-500"; }); button.className = "admin-tab-btn px-6 py-3 font-medium transition-colors border-b-2 border-blue-500 text-blue-600"; // Показ/скрытие содержимого tabContents.forEach((content) => { content.classList.add("hidden"); }); document.getElementById(tabId + "Tab").classList.remove("hidden"); // Загрузка данных при переключении switch (tabId) { case "dashboard": updateDashboard(); break; case "reports": loadReports(); break; case "users": loadUsers(); break; case "stores": loadStores(); break; case "todo": loadTodos(); break; } }); }); } // Обновление дашборда function updateDashboard() { const reports = database.reports; // Расчет статистики const totalRevenue = reports.reduce((sum, r) => sum + r.totalIncome, 0); const totalExpenses = reports.reduce((sum, r) => sum + r.totalExpenses, 0); const totalReports = reports.length; const totalUsers = database.users.filter((u) => u.role === "employee").length; // Обновление карточек document.getElementById( "totalRevenueCard" ).textContent = `€${totalRevenue.toFixed(2)}`; document.getElementById( "totalExpensesCard" ).textContent = `€${totalExpenses.toFixed(2)}`; document.getElementById("totalReportsCard").textContent = totalReports; document.getElementById("totalUsersCard").textContent = totalUsers; // Создание графиков createCharts(); } // Создание графиков function createCharts() { // График доходов по дням const revenueCtx = document.getElementById("revenueChart"); if (revenueCtx) { const last7Days = []; const revenueData = []; for (let i = 6; i >= 0; i--) { const date = new Date(); date.setDate(date.getDate() - i); const dateStr = date.toISOString().split("T")[0]; last7Days.push(dateStr); const dayReports = database.reports.filter((r) => r.date === dateStr); const dayRevenue = dayReports.reduce((sum, r) => sum + r.totalIncome, 0); revenueData.push(dayRevenue); } new Chart(revenueCtx, { type: "line", data: { labels: last7Days, datasets: [ { label: "Доходы", data: revenueData, borderColor: "rgb(59, 130, 246)", backgroundColor: "rgba(59, 130, 246, 0.1)", tension: 0.4, }, ], }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false, }, }, scales: { y: { beginAtZero: true, ticks: { callback: function (value) { return "€" + value.toFixed(0); }, }, }, }, }, }); } // Круговая диаграмма расходов const expensesCtx = document.getElementById("expensesChart"); if (expensesCtx) { const totalWages = database.reports.reduce( (sum, r) => sum + r.totalWages, 0 ); const totalInternal = database.reports.reduce( (sum, r) => sum + r.totalExpensesInternal, 0 ); new Chart(expensesCtx, { type: "doughnut", data: { labels: ["Зарплаты", "Прочие расходы"], datasets: [ { data: [totalWages, totalInternal], backgroundColor: ["#F59E0B", "#EF4444"], }, ], }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: "bottom", }, }, }, }); } // График по магазинам const storesCtx = document.getElementById("storesChart"); if (storesCtx) { const storeData = database.stores.map((store) => { const storeReports = database.reports.filter( (r) => r.storeId === store.id ); const revenue = storeReports.reduce((sum, r) => sum + r.totalIncome, 0); return { name: store.name, revenue }; }); new Chart(storesCtx, { type: "bar", data: { labels: storeData.map((s) => s.name), datasets: [ { label: "Доходы по магазинам", data: storeData.map((s) => s.revenue), backgroundColor: "#10B981", }, ], }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false, }, }, scales: { y: { beginAtZero: true, ticks: { callback: function (value) { return "€" + value.toFixed(0); }, }, }, }, }, }); } // Тренды продаж const trendsCtx = document.getElementById("trendsChart"); if (trendsCtx) { const last30Days = []; const profitData = []; for (let i = 29; i >= 0; i--) { const date = new Date(); date.setDate(date.getDate() - i); const dateStr = date.toISOString().split("T")[0]; last30Days.push(dateStr); const dayReports = database.reports.filter((r) => r.date === dateStr); const dayProfit = dayReports.reduce( (sum, r) => sum + (r.totalIncome - r.totalExpenses), 0 ); profitData.push(dayProfit); } new Chart(trendsCtx, { type: "line", data: { labels: last30Days, datasets: [ { label: "Прибыль", data: profitData, borderColor: "#8B5CF6", backgroundColor: "rgba(139, 92, 246, 0.1)", tension: 0.4, fill: true, }, ], }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false, }, }, scales: { y: { beginAtZero: true, ticks: { callback: function (value) { return "€" + value.toFixed(0); }, }, }, }, }, }); } } // Загрузка отчетов в админке // function loadReports() { // const tbody = document.getElementById("reportsTableBody"); // const filterStore = document.getElementById("filterStore"); // // Заполнение фильтра магазинов // filterStore.innerHTML = ''; // database.stores.forEach((store) => { // const option = document.createElement("option"); // option.value = store.id; // option.textContent = store.name; // filterStore.appendChild(option); // }); // // Отображение отчетов // tbody.innerHTML = ""; // database.reports.forEach((report) => { // const store = database.stores.find((s) => s.id === report.storeId); // const user = database.users.find((u) => u.id === report.userId); // const profit = report.totalIncome - report.totalExpenses; // const row = document.createElement("tr"); // row.className = "hover:bg-gray-50"; // row.innerHTML = ` // ${ // report.date // } // ${ // store ? store.name : "Неизвестно" // } // €${report.totalIncome.toFixed( // 2 // )} // €${report.totalExpenses.toFixed( // 2 // )} // €${profit.toFixed(2)} // ${ // user ? user.username : "Неизвестно" // } // // // ${report.verified ? "Проверен" : "Не проверен"} // // // // // // // `; // tbody.appendChild(row); // }); // // Настройка фильтров и экспорта // setupReportsFilters(); // } // Просмотр отчета // function viewReport(reportId) { // const report = database.reports.find((r) => r.id === reportId); // if (report) { // showReportModal(report, true); // true = админ режим // } // } // Удаление отчета function deleteReport(reportId) { if (confirm("Вы уверены, что хотите удалить этот отчет?")) { database.reports = database.reports.filter((r) => r.id !== reportId); loadReports(); updateDashboard(); showNotification("Отчет удален!"); } } // Настройка фильтров отчетов function setupReportsFilters() { document .getElementById("applyFilters") .addEventListener("click", applyReportsFilters); document .getElementById("exportExcel") .addEventListener("click", exportToExcel); } function applyReportsFilters() { const storeFilter = document.getElementById("filterStore").value; const dateFrom = document.getElementById("filterDateFrom").value; const dateTo = document.getElementById("filterDateTo").value; let filteredReports = database.reports; if (storeFilter) { filteredReports = filteredReports.filter((r) => r.storeId == storeFilter); } if (dateFrom) { filteredReports = filteredReports.filter((r) => r.date >= dateFrom); } if (dateTo) { filteredReports = filteredReports.filter((r) => r.date <= dateTo); } // Обновление таблицы с отфильтрованными данными const tbody = document.getElementById("reportsTableBody"); tbody.innerHTML = ""; filteredReports.forEach((report) => { const store = database.stores.find((s) => s.id === report.storeId); const user = database.users.find((u) => u.id === report.userId); const profit = report.totalIncome - report.totalExpenses; const row = document.createElement("tr"); row.className = "hover:bg-gray-50"; row.innerHTML = ` ${ report.date } ${ store ? store.name : "Неизвестно" } €${report.totalIncome.toFixed( 2 )} €${report.totalExpenses.toFixed( 2 )} €${profit.toFixed(2)} ${ user ? user.username : "Неизвестно" } ${report.verified ? "Проверен" : "Не проверен"} `; tbody.appendChild(row); }); showNotification(`Найдено ${filteredReports.length} отчетов`); } // Экспорт в Excel function exportToExcel() { const data = database.reports.map((report) => { const store = database.stores.find((s) => s.id === report.storeId); const user = database.users.find((u) => u.id === report.userId); const profit = report.totalIncome - report.totalExpenses; return { Дата: report.date, Магазин: store ? store.name : "Неизвестно", Доход: `€${report.totalIncome.toFixed(2)}`, Расходы: `€${report.totalExpenses.toFixed(2)}`, Прибыль: `€${profit.toFixed(2)}`, Пользователь: user ? user.username : "Неизвестно", Статус: report.verified ? "Проверен" : "Не проверен", }; }); // Создание CSV const headers = Object.keys(data[0]); const csvContent = [ headers.join(","), ...data.map((row) => headers.map((header) => `"${row[header]}"`).join(",")), ].join("\n"); // Скачивание файла const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;", }); const link = document.createElement("a"); const url = URL.createObjectURL(blob); link.setAttribute("href", url); link.setAttribute( "download", `cash_reports_${new Date().toISOString().split("T")[0]}.csv` ); link.style.visibility = "hidden"; document.body.appendChild(link); link.click(); document.body.removeChild(link); showNotification("Отчет экспортирован!"); } // === УПРАВЛЕНИЕ ПОЛЬЗОВАТЕЛЯМИ === // Загрузка пользователей // function loadUsers() { // const tbody = document.getElementById("usersTableBody"); // tbody.innerHTML = ""; // database.users.forEach((user) => { // const userStores = database.stores.filter((s) => // user.stores.includes(s.id) // ); // const storeNames = // userStores.map((s) => s.name).join(", ") || "Нет доступа"; // const row = document.createElement("tr"); // row.className = "hover:bg-gray-50"; // row.innerHTML = ` // ${user.id} // ${ // user.username // } // // // ${ // user.role === "admin" // ? "Администратор" // : "Сотрудник" // } // // // ${storeNames} // // // // // `; // tbody.appendChild(row); // }); // } // Добавление пользователя document.addEventListener("DOMContentLoaded", function () { const addUserBtn = document.getElementById("addUserBtn"); if (addUserBtn) { addUserBtn.addEventListener("click", () => { editingUserId = null; showUserEditModal(); }); } }); // Редактирование пользователя function editUser(userId) { editingUserId = userId; const user = usersList.find((u) => u.id === userId); showUserEditModal(user); } // Показ модального окна редактирования пользователя function showUserEditModal(user) { const modal = document.getElementById("userEditModal"); const title = document.getElementById("userModalTitle"); const form = document.getElementById("userEditForm"); // If editingUserId, it's an edit; else, it's add title.textContent = editingUserId ? "Редактирование пользователя" : "Добавление пользователя"; document.getElementById("userLogin").value = (user && user.username) || ""; document.getElementById("userPassword").value = ""; document.getElementById("userRole").value = (user && user.role) || "employee"; // Загрузка чекбоксов магазинов const storesContainer = document.getElementById("userStoresAccess"); storesContainer.innerHTML = ""; database.stores.forEach((store) => { const isChecked = user && user.stores && user.stores.includes(store.id); const checkbox = document.createElement("label"); checkbox.className = "flex items-center"; checkbox.innerHTML = ` ${store.name} `; storesContainer.appendChild(checkbox); }); showModal("userEditModal"); } // Сохранение пользователя document.addEventListener("DOMContentLoaded", function () { const saveUserBtn = document.getElementById("saveUserBtn"); const cancelUserBtn = document.getElementById("cancelUserBtn"); if (saveUserBtn) { saveUserBtn.addEventListener("click", saveUser); } if (cancelUserBtn) { cancelUserBtn.addEventListener("click", () => { hideModal("userEditModal"); }); } }); //save user for create and update async function saveUser() { const login = document.getElementById("userLogin").value.trim(); const password = document.getElementById("userPassword").value; const role = document.getElementById("userRole").value; const selectedStores = Array.from( document.querySelectorAll( '#userStoresAccess input[type="checkbox"]:checked' ) ).map((cb) => parseInt(cb.value)); if (!login) { showNotification("Заполните логин!", "error"); return; } // Always build userData const userData = { username: login, role: role, storeIds: selectedStores, }; if (password) userData.password = password; // Determine CREATE or EDIT let result; if (!editingUserId) { if (!password) { showNotification("Укажите пароль для нового пользователя!", "error"); return; } // CREATE result = await createUser(userData); if (result.success) { showNotification("Пользователь добавлен!"); } } else { // EDIT result = await updateUser(editingUserId, userData); if (result.success) { showNotification("Пользователь обновлен!"); } editingUserId = null; } // After save: UI update or error if (result && result.success) { hideModal("userEditModal"); loadUsers(); updateDashboard(); } else if (result) { showNotification(result.error || "Ошибка операции", "error"); } } // Удаление пользователя // function deleteUser(userId) { // const user = database.users.find((u) => u.id === userId); // const userReports = database.reports.filter((r) => r.userId === userId); // let message = `Вы уверены, что хотите удалить пользователя "${user.username}"?`; // if (userReports.length > 0) { // message += `\n\nВнимание! У этого пользователя есть ${userReports.length} связанных отчетов. Они также будут удалены.`; // } // if (confirm(message)) { // database.users = database.users.filter((u) => u.id !== userId); // database.reports = database.reports.filter((r) => r.userId !== userId); // loadUsers(); // loadReports(); // updateDashboard(); // showNotification("Пользователь и связанные отчеты удалены!"); // } // } // === УПРАВЛЕНИЕ МАГАЗИНАМИ === // Загрузка магазинов function loadStores() { const tbody = document.getElementById("storesTableBody"); tbody.innerHTML = ""; database.stores.forEach((store) => { const storeReports = database.reports.filter((r) => r.storeId === store.id); const row = document.createElement("tr"); row.className = "hover:bg-gray-50"; row.innerHTML = ` ${store.id} ${store.name} ${storeReports.length} `; tbody.appendChild(row); }); } // Добавление магазина document.addEventListener("DOMContentLoaded", function () { const addStoreBtn = document.getElementById("addStoreBtn"); if (addStoreBtn) { addStoreBtn.addEventListener("click", () => { editingStoreId = null; showStoreEditModal(); }); } }); // Редактирование магазина function editStore(storeId) { editingStoreId = storeId; showStoreEditModal(); } // Показ модального окна редактирования магазина function showStoreEditModal() { const modal = document.getElementById("storeEditModal"); const title = document.getElementById("storeModalTitle"); const form = document.getElementById("storeEditForm"); if (editingStoreId) { const store = database.stores.find((s) => s.id === editingStoreId); title.textContent = "Редактирование магазина"; document.getElementById("storeName").value = store.name; } else { title.textContent = "Добавление магазина"; form.reset(); } showModal("storeEditModal"); } // Сохранение магазина document.addEventListener("DOMContentLoaded", function () { const saveStoreBtn = document.getElementById("saveStoreBtn"); const cancelStoreBtn = document.getElementById("cancelStoreBtn"); if (saveStoreBtn) { saveStoreBtn.addEventListener("click", saveStore); } if (cancelStoreBtn) { cancelStoreBtn.addEventListener("click", () => { hideModal("storeEditModal"); }); } }); function saveStore() { const name = document.getElementById("storeName").value; if (!name) { showNotification("Заполните название магазина!", "error"); return; } if (editingStoreId) { // Редактирование const storeIndex = database.stores.findIndex( (s) => s.id === editingStoreId ); database.stores[storeIndex].name = name; showNotification("Магазин обновлен!"); } else { // Добавление const newId = Math.max(...database.stores.map((s) => s.id)) + 1; database.stores.push({ id: newId, name: name, }); showNotification("Магазин добавлен!"); } hideModal("storeEditModal"); loadStores(); loadUsers(); // Обновить список пользователей для отображения новых магазинов loadUserStores(); // Обновить список магазинов в форме пользователя updateDashboard(); } // Удаление магазина function deleteStore(storeId) { const store = database.stores.find((s) => s.id === storeId); const storeReports = database.reports.filter((r) => r.storeId === storeId); let message = `Вы уверены, что хотите удалить магазин "${store.name}"?`; if (storeReports.length > 0) { message += `\n\nВнимание! У этого магазина есть ${storeReports.length} связанных отчетов. Они также будут удалены.`; } if (confirm(message)) { database.stores = database.stores.filter((s) => s.id !== storeId); database.reports = database.reports.filter((r) => r.storeId !== storeId); // Удаление магазина из доступов пользователей database.users.forEach((user) => { user.stores = user.stores.filter((sid) => sid !== storeId); }); loadStores(); loadUsers(); loadReports(); updateDashboard(); showNotification("Магазин и связанные отчеты удалены!"); } } // === TODO СИСТЕМА === // Загрузка TODO function loadTodos() { const container = document.getElementById("todoList"); container.innerHTML = ""; database.todos.forEach((todo) => { const todoItem = document.createElement("div"); todoItem.className = `p-4 border rounded-lg ${ todo.completed ? "bg-green-50 border-green-200" : "bg-white border-gray-200" }`; const priorityColors = { low: "bg-blue-100 text-blue-800", medium: "bg-yellow-100 text-yellow-800", high: "bg-red-100 text-red-800", }; const priorityText = { low: "Низкий", medium: "Средний", high: "Высокий", }; todoItem.innerHTML = `

${todo.title}

${ todo.description }

${priorityText[todo.priority]} ${ todo.createdAt }
`; container.appendChild(todoItem); }); } // Переключение статуса TODO function toggleTodo(todoId) { const todoIndex = database.todos.findIndex((t) => t.id === todoId); database.todos[todoIndex].completed = !database.todos[todoIndex].completed; loadTodos(); showNotification("Статус задачи обновлен!"); } // Добавление TODO document.addEventListener("DOMContentLoaded", function () { const addTodoBtn = document.getElementById("addTodoBtn"); if (addTodoBtn) { addTodoBtn.addEventListener("click", () => { editingTodoId = null; showTodoEditModal(); }); } }); // Редактирование TODO function editTodo(todoId) { editingTodoId = todoId; showTodoEditModal(); } // Показ модального окна редактирования TODO function showTodoEditModal() { const modal = document.getElementById("todoEditModal"); const title = document.getElementById("todoModalTitle"); const form = document.getElementById("todoEditForm"); if (editingTodoId) { const todo = database.todos.find((t) => t.id === editingTodoId); title.textContent = "Редактирование задачи"; document.getElementById("todoTitle").value = todo.title; document.getElementById("todoDescription").value = todo.description; document.getElementById("todoPriority").value = todo.priority; } else { title.textContent = "Добавление задачи"; form.reset(); } showModal("todoEditModal"); } // Сохранение TODO document.addEventListener("DOMContentLoaded", function () { const saveTodoBtn = document.getElementById("saveTodoBtn"); const cancelTodoBtn = document.getElementById("cancelTodoBtn"); if (saveTodoBtn) { saveTodoBtn.addEventListener("click", saveTodo); } if (cancelTodoBtn) { cancelTodoBtn.addEventListener("click", () => { hideModal("todoEditModal"); }); } }); function saveTodo() { const title = document.getElementById("todoTitle").value; const description = document.getElementById("todoDescription").value; const priority = document.getElementById("todoPriority").value; if (!title) { showNotification("Заполните заголовок!", "error"); return; } if (editingTodoId) { // Редактирование const todoIndex = database.todos.findIndex((t) => t.id === editingTodoId); database.todos[todoIndex].title = title; database.todos[todoIndex].description = description; database.todos[todoIndex].priority = priority; showNotification("Задача обновлена!"); } else { // Добавление const newId = Math.max(...database.todos.map((t) => t.id)) + 1; database.todos.push({ id: newId, title: title, description: description, priority: priority, completed: false, createdAt: new Date().toISOString().split("T")[0], }); showNotification("Задача добавлена!"); } hideModal("todoEditModal"); loadTodos(); } // Удаление TODO function deleteTodo(todoId) { if (confirm("Вы уверены, что хотите удалить эту задачу?")) { database.todos = database.todos.filter((t) => t.id !== todoId); loadTodos(); showNotification("Задача удалена!"); } } // Обработчики выхода document.getElementById("logoutBtn").addEventListener("click", logout); document.getElementById("adminLogoutBtn").addEventListener("click", logout); function logout() { currentUser = null; editingReportId = null; document.getElementById("loginScreen").classList.remove("hidden"); document.getElementById("userInterface").classList.add("hidden"); document.getElementById("adminInterface").classList.add("hidden"); document.getElementById("loginForm").reset(); document.getElementById("loginError").classList.add("hidden"); showNotification("Вы вышли из системы"); } // Глобальные функции для onclick обработчиков window.viewReport = viewReport; window.deleteReport = deleteReport; window.editUser = editUser; window.deleteUser = deleteUser; window.editStore = editStore; window.deleteStore = deleteStore; window.editTodo = editTodo; window.deleteTodo = deleteTodo; window.toggleTodo = toggleTodo;