// Глобальные переменные
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 = `
`;
} else {
notification.innerHTML = `
`;
}
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 = `
`;
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;