Add files via upload

v0.0.3
This commit is contained in:
cyrteL
2025-07-28 17:31:27 +03:00
committed by GitHub
parent 119a74ad98
commit 4a6868ced6
17 changed files with 2395 additions and 292 deletions

View File

@ -39,14 +39,17 @@ class AuthSystem {
try {
this.currentUser = JSON.parse(userData);
this.isAuthenticated = true;
console.log('Пользователь авторизован:', this.currentUser);
this.updateUI();
} catch (error) {
console.error('Ошибка при загрузке данных пользователя:', error);
this.logout();
}
} else {
console.log('Пользователь не авторизован');
// Если нет сессии и мы не на странице входа, перенаправляем
if (!window.location.pathname.includes('login.html')) {
console.log('Перенаправление на страницу входа');
window.location.href = 'login.html';
}
}
@ -54,8 +57,8 @@ class AuthSystem {
// Вход в систему
login() {
const username = $('#username').val();
const password = $('#password').val();
const username = $('#username').val().trim();
const password = $('#password').val().trim();
if (!username || !password) {
this.showNotification('Пожалуйста, заполните все поля', 'warning');
@ -77,11 +80,24 @@ class AuthSystem {
this.showNotification('Успешный вход в систему', 'success');
// Скрываем индикатор загрузки и восстанавливаем кнопку
$('#loginLoading').hide();
$('#loginBtn').prop('disabled', false);
// Перенаправляем на главную страницу
setTimeout(() => {
window.location.href = 'index.html';
}, 1000);
} else {
// Скрываем индикатор загрузки и восстанавливаем кнопку
$('#loginLoading').hide();
$('#loginBtn').prop('disabled', false);
// Показываем ошибку в полях
$('#username').addClass('is-invalid');
$('#password').addClass('is-invalid');
$('#usernameError').text('Неверное имя пользователя или пароль').show();
this.showNotification('Неверное имя пользователя или пароль', 'danger');
}
}
@ -101,18 +117,29 @@ class AuthSystem {
// Обновление интерфейса в зависимости от прав доступа
updateUI() {
if (!this.currentUser) return;
if (!this.currentUser) {
console.log('Нет текущего пользователя для обновления UI');
return;
}
console.log('Обновление UI для пользователя:', this.currentUser.name);
// Обновляем информацию о пользователе в навигации
$('#profileDropdown .dropdown-toggle').html(`
<i class="fas fa-user me-1"></i>${this.currentUser.name}
`);
const profileToggle = $('#profileDropdown .dropdown-toggle');
if (profileToggle.length) {
profileToggle.html(`<i class="fas fa-user me-1"></i>${this.currentUser.name}`);
}
// Показываем профиль для всех авторизованных пользователей
$('#profileDropdown').show();
// Показываем/скрываем элементы администрирования
if (this.hasRole('admin')) {
$('#adminDropdown').show();
console.log('Показана панель администратора');
} else {
$('#adminDropdown').hide();
console.log('Скрыта панель администратора');
}
// Обновляем права доступа для кнопок
@ -188,7 +215,7 @@ class AuthSystem {
// Проверка аутентификации
isUserAuthenticated() {
return this.isAuthenticated;
return this.isAuthenticated && this.currentUser !== null;
}
// Получение списка пользователей (имитация базы данных)

View File

@ -1,4 +1,4 @@
// JavaScript для страницы каталога товаров
// JavaScript для страницы каталога позиций
class CatalogManager {
constructor() {
@ -14,10 +14,45 @@ class CatalogManager {
}
init() {
$(document).ready(() => {
this.loadCatalog();
this.bindEvents();
this.initializeFilters();
$(document).ready(async () => {
try {
// Проверяем авторизацию
if (!auth.isUserAuthenticated()) {
console.log('Пользователь не авторизован, перенаправление на login.html');
window.location.href = 'login.html';
return;
}
console.log('Инициализация каталога для пользователя:', auth.getCurrentUser()?.name);
// Загружаем каталог
await this.loadCatalog();
// Загружаем категории в навигацию
await this.loadCategoriesNav();
// Загружаем категории в выпадающее меню
await this.loadCategoriesDropdown();
// Обновляем информацию о пользователе
this.updateUserInfo();
// Привязываем события
this.bindEvents();
// Инициализируем фильтры
this.initializeFilters();
// Проверяем параметр категории в URL
this.handleCategoryFromUrl();
// Обновляем права доступа
this.updatePermissions();
} catch (error) {
console.error('Ошибка инициализации:', error);
auth.showNotification('Ошибка загрузки данных', 'danger');
}
});
}
@ -41,6 +76,13 @@ class CatalogManager {
// Экспорт
$('#exportBtn').on('click', () => this.exportCatalog());
// Обработчик выхода
$('#logoutBtn').on('click', (e) => {
e.preventDefault();
auth.logout();
window.location.href = 'login.html';
});
}
// Загрузка каталога
@ -48,12 +90,17 @@ class CatalogManager {
try {
this.showLoading();
// Загружаем товары
// Загружаем позиции
this.currentItems = await this.warehouse.getAllItems();
// Загружаем категории
const categories = await this.warehouse.getCategories();
this.populateCategoryFilters(categories);
if (categories && categories.length > 0) {
this.populateCategoryFilters(categories);
} else {
console.warn('Категории не найдены');
auth.showNotification('Категории не загружены', 'warning');
}
// Применяем фильтры и рендерим
this.applyFilters();
@ -83,6 +130,11 @@ class CatalogManager {
// Заполнение фильтра категорий
populateCategoryFilters(categories) {
if (!categories || categories.length === 0) {
console.warn('Нет категорий для отображения');
return;
}
const categoryFilter = $('#categoryFilter');
const addItemCategory = $('#itemCategory');
const editItemCategory = $('#editItemCategory');
@ -94,10 +146,12 @@ class CatalogManager {
// Добавляем категории
categories.forEach(category => {
const option = `<option value="${category.id}">${category.name}</option>`;
categoryFilter.append(option);
addItemCategory.append(option);
editItemCategory.append(option);
if (category && category.id && category.name) {
const option = `<option value="${category.id}">${category.name}</option>`;
categoryFilter.append(option);
addItemCategory.append(option);
editItemCategory.append(option);
}
});
}
@ -125,7 +179,7 @@ class CatalogManager {
this.filters.inStock = $('#inStockFilter').is(':checked');
this.filters.lowStock = $('#lowStockFilter').is(':checked');
// Фильтруем товары
// Фильтруем позиции
this.filteredItems = this.currentItems.filter(item => {
// Поиск по названию, описанию и штрих-коду
if (this.filters.search) {
@ -191,7 +245,7 @@ class CatalogManager {
<div class="col-12">
<div class="empty-state">
<i class="fas fa-search"></i>
<h4>Товары не найдены</h4>
<h4>Позиции не найдены</h4>
<p>Попробуйте изменить параметры поиска или фильтры</p>
</div>
</div>
@ -205,7 +259,7 @@ class CatalogManager {
const endIndex = startIndex + this.itemsPerPage;
const pageItems = this.filteredItems.slice(startIndex, endIndex);
// Рендерим товары
// Рендерим позиции
pageItems.forEach(item => {
const itemHtml = this.currentView === 'grid' ?
this.createGridItem(item) : this.createListItem(item);
@ -216,7 +270,7 @@ class CatalogManager {
this.renderPagination(totalPages);
}
// Создание карточки товара (сетка)
// Создание карточки позиции (сетка)
createGridItem(item) {
const category = this.getCategoryInfo(item.category);
const stockStatus = this.getStockStatus(item);
@ -248,7 +302,7 @@ class CatalogManager {
<button class="btn btn-outline-primary btn-sm" onclick="catalogManager.viewItem(${item.id})">
<i class="fas fa-eye me-1"></i>Просмотр
</button>
${auth.hasPermission('write') ? `
${(auth.hasRole('admin') || auth.hasRole('manager')) ? `
<button class="btn btn-outline-warning btn-sm" onclick="catalogManager.editItem(${item.id})">
<i class="fas fa-edit me-1"></i>Редактировать
</button>
@ -260,7 +314,7 @@ class CatalogManager {
`;
}
// Создание строки товара (список)
// Создание строки позиции (список)
createListItem(item) {
const category = this.getCategoryInfo(item.category);
const stockStatus = this.getStockStatus(item);
@ -300,12 +354,12 @@ class CatalogManager {
<li><a class="dropdown-item" href="#" onclick="catalogManager.viewItem(${item.id})">
<i class="fas fa-eye me-2"></i>Просмотр
</a></li>
${auth.hasPermission('write') ? `
${(auth.hasRole('admin') || auth.hasRole('manager')) ? `
<li><a class="dropdown-item" href="#" onclick="catalogManager.editItem(${item.id})">
<i class="fas fa-edit me-2"></i>Редактировать
</a></li>
` : ''}
${auth.hasPermission('delete') ? `
${auth.hasRole('admin') ? `
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item text-danger" href="#" onclick="catalogManager.deleteItem(${item.id})">
<i class="fas fa-trash me-2"></i>Удалить
@ -370,10 +424,14 @@ class CatalogManager {
// Обновление статистики
updateStatistics() {
if (!this.filteredItems) {
this.filteredItems = [];
}
const totalItems = this.filteredItems.length;
const inStockItems = this.filteredItems.filter(item => item.quantity > 0).length;
const lowStockItems = this.filteredItems.filter(item => item.quantity <= item.minQuantity).length;
const categories = [...new Set(this.filteredItems.map(item => item.category))].length;
const inStockItems = this.filteredItems.filter(item => item && item.quantity > 0).length;
const lowStockItems = this.filteredItems.filter(item => item && item.quantity <= (item.minQuantity || 0)).length;
const categories = [...new Set(this.filteredItems.filter(item => item && item.category).map(item => item.category))].length;
$('#totalItemsCount').text(totalItems);
$('#inStockCount').text(inStockItems);
@ -383,17 +441,21 @@ class CatalogManager {
// Получение информации о категории
getCategoryInfo(categoryId) {
const categories = [
{ id: 'electronics', name: 'Электроника', icon: 'fas fa-laptop' },
{ id: 'clothing', name: 'Одежда', icon: 'fas fa-tshirt' },
{ id: 'tools', name: 'Инструменты', icon: 'fas fa-tools' },
{ id: 'office', name: 'Офисные принадлежности', icon: 'fas fa-briefcase' },
{ id: 'furniture', name: 'Мебель', icon: 'fas fa-couch' },
{ id: 'books', name: 'Книги', icon: 'fas fa-book' }
];
if (!categoryId) {
return { id: 'unknown', name: 'Без категории', icon: 'fas fa-box' };
}
// Используем категории из warehouse
const category = this.warehouse.categories.find(cat => cat.id === categoryId);
if (category) {
return {
id: category.id,
name: category.name,
icon: category.icon || 'fas fa-box'
};
}
return categories.find(cat => cat.id === categoryId) ||
{ id: 'unknown', name: 'Неизвестная категория', icon: 'fas fa-box' };
return { id: 'unknown', name: 'Неизвестная категория', icon: 'fas fa-box' };
}
// Получение статуса запаса
@ -407,16 +469,16 @@ class CatalogManager {
}
}
// Просмотр товара
// Просмотр позиции
viewItem(itemId) {
const item = this.currentItems.find(i => i.id === itemId);
if (!item) return;
// Здесь можно открыть модальное окно с подробной информацией
auth.showNotification(`Просмотр товара: ${item.name}`, 'info');
auth.showNotification(`Просмотр позиции: ${item.name}`, 'info');
}
// Редактирование товара
// Редактирование позиции
async editItem(itemId) {
const item = this.currentItems.find(i => i.id === itemId);
if (!item) return;
@ -467,19 +529,19 @@ class CatalogManager {
this.currentItems[itemIndex] = { ...this.currentItems[itemIndex], ...updateData };
}
auth.showNotification('Товар успешно обновлен', 'success');
auth.showNotification('Позиция успешно обновлена', 'success');
// Закрываем модальное окно и обновляем каталог
$('#editItemModal').modal('hide');
this.applyFilters();
} catch (error) {
console.error('Ошибка при обновлении товара:', error);
auth.showNotification('Ошибка при обновлении товара', 'danger');
console.error('Ошибка при обновлении позиции:', error);
auth.showNotification('Ошибка при обновлении позиции', 'danger');
}
}
// Добавление нового товара
// Добавление новой позиции
async addNewItem() {
const formData = this.getFormData('#addItemForm');
@ -493,7 +555,7 @@ class CatalogManager {
// Добавляем в локальные данные
this.currentItems.push(newItem);
auth.showNotification('Товар успешно добавлен', 'success');
auth.showNotification('Позиция успешно добавлена', 'success');
// Закрываем модальное окно и обновляем каталог
$('#addItemModal').modal('hide');
@ -503,14 +565,14 @@ class CatalogManager {
$('#addItemForm')[0].reset();
} catch (error) {
console.error('Ошибка при добавлении товара:', error);
auth.showNotification('Ошибка при добавлении товара', 'danger');
console.error('Ошибка при добавлении позиции:', error);
auth.showNotification('Ошибка при добавлении позиции', 'danger');
}
}
// Удаление товара
// Удаление позиции
async deleteItem(itemId) {
if (!confirm('Вы уверены, что хотите удалить этот товар?')) {
if (!confirm('Вы уверены, что хотите удалить эту позицию?')) {
return;
}
@ -520,12 +582,12 @@ class CatalogManager {
// Удаляем из локальных данных
this.currentItems = this.currentItems.filter(i => i.id !== itemId);
auth.showNotification('Товар успешно удален', 'success');
auth.showNotification('Позиция успешно удалена', 'success');
this.applyFilters();
} catch (error) {
console.error('Ошибка при удалении товара:', error);
auth.showNotification('Ошибка при удалении товара', 'danger');
console.error('Ошибка при удалении позиции:', error);
auth.showNotification('Ошибка при удалении позиции', 'danger');
}
}
@ -632,6 +694,126 @@ class CatalogManager {
hideLoading() {
// Загрузка завершается в renderCatalog()
}
// Обновление информации о пользователе
updateUserInfo() {
const user = auth.getCurrentUser();
console.log('Обновление информации о пользователе в каталоге:', user);
if (user) {
// Обновляем имя пользователя в профиле
const profileToggle = $('#profileDropdown .dropdown-toggle');
if (profileToggle.length) {
profileToggle.html(`<i class="fas fa-user me-1"></i>${user.name}`);
}
// Показываем профиль для всех авторизованных пользователей
$('#profileDropdown').show();
// Показываем админ панель только для администраторов
if (auth.hasRole('admin')) {
$('#adminDropdown').show();
console.log('Показана панель администратора в каталоге');
} else {
$('#adminDropdown').hide();
console.log('Скрыта панель администратора в каталоге');
}
} else {
console.log('Пользователь не авторизован в каталоге');
$('#profileDropdown').hide();
$('#adminDropdown').hide();
}
}
// Загрузка категорий в навигацию
async loadCategoriesNav() {
try {
const categories = await this.warehouse.getCategories();
const categoriesNav = $('#categoriesNav');
// Очищаем существующие категории (кроме "Главная")
categoriesNav.find('li:not(:first-child)').remove();
// Добавляем активные категории
categories.forEach(category => {
if (category.active) {
const categoryLink = `
<li class="nav-item">
<a class="nav-link" href="catalog.html?category=${category.id}" title="${category.description || ''}">
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
${category.name}
</a>
</li>
`;
categoriesNav.append(categoryLink);
}
});
} catch (error) {
console.error('Ошибка загрузки категорий:', error);
}
}
// Обновление прав доступа
updatePermissions() {
const user = auth.getCurrentUser();
if (!user) return;
// Показываем кнопку добавления для администраторов и менеджеров
if (auth.hasRole('admin') || auth.hasRole('manager')) {
$('#addItemBtn').show();
} else {
$('#addItemBtn').hide();
}
// Показываем кнопку экспорта для всех авторизованных пользователей
$('#exportBtn').show();
}
// Загрузка категорий в выпадающее меню
async loadCategoriesDropdown() {
try {
const categories = await this.warehouse.getCategories();
const dropdown = $('#catalogCategoriesDropdown');
// Очищаем существующие категории (кроме "Все позиции" и разделителя)
dropdown.find('li:not(:first-child):not(:nth-child(2))').remove();
// Добавляем активные категории
categories.forEach(category => {
if (category.active) {
const categoryLink = `
<li>
<a class="dropdown-item" href="catalog.html?category=${category.id}" title="${category.description || ''}">
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
${category.name}
</a>
</li>
`;
dropdown.append(categoryLink);
}
});
} catch (error) {
console.error('Ошибка загрузки категорий в выпадающее меню:', error);
}
}
// Обработка параметра категории из URL
handleCategoryFromUrl() {
const urlParams = new URLSearchParams(window.location.search);
const categoryParam = urlParams.get('category');
if (categoryParam) {
// Устанавливаем фильтр категории
$('#categoryFilter').val(categoryParam);
// Применяем фильтры
this.applyFilters();
// Показываем уведомление о примененном фильтре
const categoryInfo = this.getCategoryInfo(categoryParam);
auth.showNotification(`Отфильтровано по категории: ${categoryInfo.name}`, 'info');
}
}
}
// Инициализация менеджера каталога

403
js/categories.js Normal file
View File

@ -0,0 +1,403 @@
// JavaScript для управления категориями
class CategoryManager {
constructor() {
this.warehouse = new WarehouseManager();
this.categories = [];
this.currentCategory = null;
this.init();
}
async init() {
try {
// Проверяем авторизацию
if (!auth.isUserAuthenticated()) {
console.log('Пользователь не авторизован, перенаправление на login.html');
window.location.href = '../login.html';
return;
}
// Проверяем права доступа
if (!auth.hasRole('admin')) {
console.log('Пользователь не имеет прав администратора');
auth.showNotification('Недостаточно прав для доступа к этой странице', 'danger');
window.location.href = '../index.html';
return;
}
console.log('Инициализация управления категориями для администратора:', auth.getCurrentUser()?.name);
// Загружаем данные
await this.loadCategories();
this.updateStatistics();
this.renderCategoriesTable();
this.bindEvents();
// Загружаем категории в выпадающее меню
await this.loadCategoriesDropdown();
this.updateUserInfo();
} catch (error) {
console.error('Ошибка инициализации:', error);
auth.showNotification('Ошибка загрузки данных', 'danger');
}
}
// Загрузка категорий
async loadCategories() {
this.categories = await this.warehouse.getCategories();
}
// Обновление статистики
updateStatistics() {
const totalCategories = this.categories.length;
const activeCategories = this.categories.filter(cat => cat.active).length;
const emptyCategories = this.categories.filter(cat => cat.itemCount === 0).length;
// Получаем общее количество позиций
const allItems = this.warehouse.getAllItemsSync();
const totalItems = allItems.length;
$('#totalCategories').text(totalCategories);
$('#totalItems').text(totalItems);
$('#activeCategories').text(activeCategories);
$('#emptyCategories').text(emptyCategories);
}
// Рендеринг таблицы категорий
renderCategoriesTable() {
const tbody = $('#categoriesTable tbody');
tbody.empty();
if (this.categories.length === 0) {
tbody.append(`
<tr>
<td colspan="7" class="text-center py-4">
<div class="empty-state">
<i class="fas fa-tags fa-3x text-muted mb-3"></i>
<h5>Категории не найдены</h5>
<p class="text-muted">Добавьте первую категорию для начала работы</p>
</div>
</td>
</tr>
`);
return;
}
this.categories.forEach(category => {
const row = this.createCategoryRow(category);
tbody.append(row);
});
}
// Создание строки категории
createCategoryRow(category) {
const statusBadge = category.active ?
'<span class="badge bg-success">Активна</span>' :
'<span class="badge bg-secondary">Неактивна</span>';
const iconPreview = category.icon ?
`<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>` :
'<i class="fas fa-tag text-muted"></i>';
return `
<tr data-category-id="${category.id}">
<td>${category.id}</td>
<td>
<div class="d-flex align-items-center">
<span class="me-2">${iconPreview}</span>
<strong>${category.name}</strong>
</div>
</td>
<td>${category.description || '-'}</td>
<td>
<code>${category.icon || 'fas fa-tag'}</code>
</td>
<td>
<span class="badge bg-info">${category.itemCount || 0}</span>
</td>
<td>${statusBadge}</td>
<td>
<div class="btn-group btn-group-sm">
<button class="btn btn-outline-primary" onclick="categoryManager.editCategory('${category.id}')" title="Редактировать">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-outline-danger" onclick="categoryManager.deleteCategory('${category.id}')" title="Удалить">
<i class="fas fa-trash"></i>
</button>
</div>
</td>
</tr>
`;
}
// Привязка событий
bindEvents() {
// Добавление категории
$('#saveCategoryBtn').on('click', () => this.addCategory());
// Редактирование категории
$('#updateCategoryBtn').on('click', () => this.updateCategory());
// Очистка формы при закрытии модального окна
$('#addCategoryModal').on('hidden.bs.modal', () => this.clearAddForm());
$('#editCategoryModal').on('hidden.bs.modal', () => this.clearEditForm());
// Валидация формы
$('#addCategoryForm').on('submit', (e) => {
e.preventDefault();
this.addCategory();
});
$('#editCategoryForm').on('submit', (e) => {
e.preventDefault();
this.updateCategory();
});
}
// Добавление категории
async addCategory() {
try {
const formData = this.getAddFormData();
if (!formData.name.trim()) {
auth.showNotification('Введите название категории', 'warning');
return;
}
// Проверяем уникальность названия
const existingCategory = this.categories.find(cat =>
cat.name.toLowerCase() === formData.name.toLowerCase()
);
if (existingCategory) {
auth.showNotification('Категория с таким названием уже существует', 'warning');
return;
}
const newCategory = await this.warehouse.addCategory(formData);
this.categories.push(newCategory);
this.updateStatistics();
this.renderCategoriesTable();
$('#addCategoryModal').modal('hide');
auth.showNotification('Категория успешно добавлена', 'success');
} catch (error) {
console.error('Ошибка при добавлении категории:', error);
auth.showNotification('Ошибка при добавлении категории', 'danger');
}
}
// Редактирование категории
async editCategory(categoryId) {
try {
const category = this.categories.find(cat => cat.id === categoryId);
if (!category) {
auth.showNotification('Категория не найдена', 'danger');
return;
}
this.currentCategory = category;
this.fillEditForm(category);
$('#editCategoryModal').modal('show');
} catch (error) {
console.error('Ошибка при редактировании категории:', error);
auth.showNotification('Ошибка при загрузке категории', 'danger');
}
}
// Обновление категории
async updateCategory() {
try {
if (!this.currentCategory) {
auth.showNotification('Категория не выбрана', 'warning');
return;
}
const formData = this.getEditFormData();
if (!formData.name.trim()) {
auth.showNotification('Введите название категории', 'warning');
return;
}
// Проверяем уникальность названия (исключая текущую категорию)
const existingCategory = this.categories.find(cat =>
cat.id !== this.currentCategory.id &&
cat.name.toLowerCase() === formData.name.toLowerCase()
);
if (existingCategory) {
auth.showNotification('Категория с таким названием уже существует', 'warning');
return;
}
const updatedCategory = await this.warehouse.updateCategory(this.currentCategory.id, formData);
// Обновляем категорию в списке
const index = this.categories.findIndex(cat => cat.id === this.currentCategory.id);
if (index !== -1) {
this.categories[index] = updatedCategory;
}
this.updateStatistics();
this.renderCategoriesTable();
$('#editCategoryModal').modal('hide');
auth.showNotification('Категория успешно обновлена', 'success');
} catch (error) {
console.error('Ошибка при обновлении категории:', error);
auth.showNotification('Ошибка при обновлении категории', 'danger');
}
}
// Удаление категории
async deleteCategory(categoryId) {
try {
const category = this.categories.find(cat => cat.id === categoryId);
if (!category) {
auth.showNotification('Категория не найдена', 'danger');
return;
}
// Проверяем, есть ли позиции в этой категории
if (category.itemCount > 0) {
const confirmDelete = confirm(
`В категории "${category.name}" есть ${category.itemCount} позиций. ` +
'Удаление категории приведет к удалению всех позиций. Продолжить?'
);
if (!confirmDelete) {
return;
}
} else {
const confirmDelete = confirm(`Вы уверены, что хотите удалить категорию "${category.name}"?`);
if (!confirmDelete) {
return;
}
}
await this.warehouse.deleteCategory(categoryId);
// Удаляем категорию из списка
this.categories = this.categories.filter(cat => cat.id !== categoryId);
this.updateStatistics();
this.renderCategoriesTable();
auth.showNotification('Категория успешно удалена', 'success');
} catch (error) {
console.error('Ошибка при удалении категории:', error);
auth.showNotification('Ошибка при удалении категории', 'danger');
}
}
// Получение данных формы добавления
getAddFormData() {
return {
name: $('#categoryName').val().trim(),
description: $('#categoryDescription').val().trim(),
icon: $('#categoryIcon').val().trim() || 'fas fa-tag',
color: $('#categoryColor').val(),
active: $('#categoryActive').is(':checked')
};
}
// Получение данных формы редактирования
getEditFormData() {
return {
name: $('#editCategoryName').val().trim(),
description: $('#editCategoryDescription').val().trim(),
icon: $('#editCategoryIcon').val().trim() || 'fas fa-tag',
color: $('#editCategoryColor').val(),
active: $('#editCategoryActive').is(':checked')
};
}
// Заполнение формы редактирования
fillEditForm(category) {
$('#editCategoryId').val(category.id);
$('#editCategoryName').val(category.name);
$('#editCategoryDescription').val(category.description || '');
$('#editCategoryIcon').val(category.icon || 'fas fa-tag');
$('#editCategoryColor').val(category.color || '#2c5aa0');
$('#editCategoryActive').prop('checked', category.active);
}
// Очистка формы добавления
clearAddForm() {
$('#addCategoryForm')[0].reset();
$('#categoryColor').val('#2c5aa0');
$('#categoryActive').prop('checked', true);
}
// Очистка формы редактирования
clearEditForm() {
$('#editCategoryForm')[0].reset();
$('#editCategoryColor').val('#2c5aa0');
this.currentCategory = null;
}
// Обновление информации о пользователе
updateUserInfo() {
const user = auth.getCurrentUser();
if (user) {
$('#currentUserInfo').text(user.name);
$('#loginLink').hide();
$('#profileDropdown').show();
$('#adminDropdown').show();
} else {
$('#currentUserInfo').text('Гость');
$('#loginLink').show();
$('#profileDropdown').hide();
$('#adminDropdown').hide();
}
}
// Загрузка категорий в выпадающее меню
async loadCategoriesDropdown() {
try {
const categories = await this.warehouse.getCategories();
const dropdown = $('#catalogCategoriesDropdown');
// Очищаем существующие категории (кроме "Все позиции" и разделителя)
dropdown.find('li:not(:first-child):not(:nth-child(2))').remove();
// Добавляем активные категории
categories.forEach(category => {
if (category.active) {
const categoryLink = `
<li>
<a class="dropdown-item" href="../catalog.html?category=${category.id}" title="${category.description || ''}">
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
${category.name}
</a>
</li>
`;
dropdown.append(categoryLink);
}
});
} catch (error) {
console.error('Ошибка загрузки категорий в выпадающее меню:', error);
}
}
}
// Инициализация при загрузке страницы
let categoryManager;
$(document).ready(() => {
categoryManager = new CategoryManager();
// Обработка выхода
$('#logoutBtn').on('click', (e) => {
e.preventDefault();
auth.logout();
window.location.href = '../login.html';
});
});

View File

@ -13,10 +13,39 @@ class HistoryManager {
}
init() {
$(document).ready(() => {
this.loadHistory();
this.bindEvents();
this.initializeFilters();
$(document).ready(async () => {
try {
// Проверяем авторизацию
if (!auth.isUserAuthenticated()) {
console.log('Пользователь не авторизован, перенаправление на login.html');
window.location.href = 'login.html';
return;
}
console.log('Инициализация истории для пользователя:', auth.getCurrentUser()?.name);
// Загружаем историю операций
await this.loadHistory();
// Загружаем категории в навигацию
await this.loadCategoriesNav();
// Загружаем категории в выпадающее меню
await this.loadCategoriesDropdown();
// Обновляем информацию о пользователе
this.updateUserInfo();
// Привязываем события
this.bindEvents();
// Инициализируем фильтры
this.initializeFilters();
} catch (error) {
console.error('Ошибка инициализации:', error);
auth.showNotification('Ошибка загрузки данных', 'danger');
}
});
}
@ -32,6 +61,13 @@ class HistoryManager {
$('#exportBtn').on('click', () => this.exportOperations());
$('#generateReportBtn').on('click', () => this.generateReport());
// Обработчик выхода
$('#logoutBtn').on('click', (e) => {
e.preventDefault();
auth.logout();
window.location.href = 'login.html';
});
// Установка дат по умолчанию
this.setDefaultDates();
}
@ -44,7 +80,7 @@ class HistoryManager {
// Загружаем операции
this.operations = await this.warehouse.getOperations();
// Загружаем товары для фильтра
// Загружаем позиции для фильтра
const items = await this.warehouse.getAllItems();
this.populateItemFilter(items);
@ -89,7 +125,7 @@ class HistoryManager {
$('#reportEndDate').val(today.toISOString().split('T')[0]);
}
// Заполнение фильтра товаров
// Заполнение фильтра позиций
populateItemFilter(items) {
const itemFilter = $('#itemFilter');
const reportItem = $('#reportItem');
@ -147,7 +183,7 @@ class HistoryManager {
}
}
// Фильтр по товару
// Фильтр по позиции
if (this.filters.itemId && operation.itemId !== parseInt(this.filters.itemId)) {
return false;
}
@ -405,10 +441,10 @@ class HistoryManager {
</table>
</div>
<div class="col-md-6">
<h6>Детали товара</h6>
<h6>Детали позиции</h6>
<table class="table table-sm">
<tr>
<td><strong>Товар:</strong></td>
<td><strong>Позиция:</strong></td>
<td>${operation.itemName}</td>
</tr>
<tr>
@ -614,6 +650,78 @@ class HistoryManager {
hideLoading() {
// Загрузка завершается в renderOperations()
}
// Обновление информации о пользователе
updateUserInfo() {
const user = auth.getCurrentUser();
if (user) {
$('#currentUserInfo').text(user.name);
$('#loginLink').hide();
$('#profileDropdown').show();
$('#adminDropdown').show();
} else {
$('#currentUserInfo').text('Гость');
$('#loginLink').show();
$('#profileDropdown').hide();
$('#adminDropdown').hide();
}
}
// Загрузка категорий в навигацию
async loadCategoriesNav() {
try {
const categories = await this.warehouse.getCategories();
const categoriesNav = $('#categoriesNav');
// Очищаем существующие категории (кроме "Главная")
categoriesNav.find('li:not(:first-child)').remove();
// Добавляем активные категории
categories.forEach(category => {
if (category.active) {
const categoryLink = `
<li class="nav-item">
<a class="nav-link" href="catalog.html?category=${category.id}" title="${category.description || ''}">
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
${category.name}
</a>
</li>
`;
categoriesNav.append(categoryLink);
}
});
} catch (error) {
console.error('Ошибка загрузки категорий:', error);
}
}
// Загрузка категорий в выпадающее меню
async loadCategoriesDropdown() {
try {
const categories = await this.warehouse.getCategories();
const dropdown = $('#catalogCategoriesDropdown');
// Очищаем существующие категории (кроме "Все позиции" и разделителя)
dropdown.find('li:not(:first-child):not(:nth-child(2))').remove();
// Добавляем активные категории
categories.forEach(category => {
if (category.active) {
const categoryLink = `
<li>
<a class="dropdown-item" href="catalog.html?category=${category.id}" title="${category.description || ''}">
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
${category.name}
</a>
</li>
`;
dropdown.append(categoryLink);
}
});
} catch (error) {
console.error('Ошибка загрузки категорий в выпадающее меню:', error);
}
}
}
// Инициализация менеджера истории

View File

@ -8,10 +8,44 @@ class MainApp {
init() {
// Инициализация после загрузки DOM
$(document).ready(() => {
this.loadDashboardData();
this.bindEvents();
this.initializeModals();
$(document).ready(async () => {
try {
// Проверяем авторизацию
if (!auth.isUserAuthenticated()) {
console.log('Пользователь не авторизован, перенаправление на login.html');
window.location.href = 'login.html';
return;
}
console.log('Инициализация главной страницы для пользователя:', auth.getCurrentUser()?.name);
// Загружаем данные дашборда
await this.loadDashboardData();
// Загружаем категории в навигацию
await this.loadCategoriesNav();
// Загружаем категории в выпадающее меню
await this.loadCategoriesDropdown();
// Обновляем информацию о пользователе
this.updateUserInfo();
// Принудительно обновляем еще раз через небольшую задержку
setTimeout(() => {
this.updateUserInfo();
}, 500);
// Привязываем события
this.bindEvents();
// Инициализируем модальные окна
this.initializeModals();
} catch (error) {
console.error('Ошибка инициализации:', error);
auth.showNotification('Ошибка загрузки данных', 'danger');
}
});
}
@ -29,6 +63,13 @@ class MainApp {
// Обработчики для экспорта
$('#exportBtn').on('click', () => this.exportData());
// Обработчик выхода
$('#logoutBtn').on('click', (e) => {
e.preventDefault();
auth.logout();
window.location.href = 'login.html';
});
}
// Загрузка данных для дашборда
@ -38,14 +79,14 @@ class MainApp {
this.showLoading();
// Загружаем статистику
const stats = await this.warehouse.getStatistics();
const stats = this.warehouse.getStatistics();
this.updateStatistics(stats);
// Загружаем последние операции
const operations = await this.warehouse.getRecentOperations(10);
this.updateOperationsTable(operations);
// Загружаем товары для модальных окон
// Загружаем позиции для модальных окон
const items = await this.warehouse.getAllItems();
this.populateItemSelects(items);
@ -146,7 +187,7 @@ class MainApp {
});
}
// Заполнение селектов товаров
// Заполнение селектов позиций
populateItemSelects(items) {
const selects = ['#incomingItem', '#outgoingItem'];
@ -160,7 +201,7 @@ class MainApp {
});
}
// Добавление нового товара
// Добавление новой позиции
async addNewItem() {
const formData = this.getFormData('#addItemForm');
@ -170,7 +211,7 @@ class MainApp {
try {
const newItem = await this.warehouse.addItem(formData);
auth.showNotification('Товар успешно добавлен', 'success');
auth.showNotification('Позиция успешно добавлена', 'success');
// Закрываем модальное окно
$('#addItemModal').modal('hide');
@ -182,8 +223,8 @@ class MainApp {
$('#addItemForm')[0].reset();
} catch (error) {
console.error('Ошибка при добавлении товара:', error);
auth.showNotification('Ошибка при добавлении товара', 'danger');
console.error('Ошибка при добавлении позиции:', error);
auth.showNotification('Ошибка при добавлении позиции', 'danger');
}
}
@ -424,6 +465,92 @@ class MainApp {
hideLoading() {
$('#loadingOverlay').remove();
}
// Обновление информации о пользователе
updateUserInfo() {
const user = auth.getCurrentUser();
console.log('Обновление информации о пользователе:', user);
if (user) {
// Обновляем имя пользователя в профиле
const profileToggle = $('#profileDropdown .dropdown-toggle');
if (profileToggle.length) {
profileToggle.html(`<i class="fas fa-user me-1"></i>${user.name}`);
}
// Показываем профиль для всех авторизованных пользователей
$('#profileDropdown').show();
// Показываем админ панель только для администраторов
if (auth.hasRole('admin')) {
$('#adminDropdown').show();
console.log('Показана панель администратора');
} else {
$('#adminDropdown').hide();
console.log('Скрыта панель администратора');
}
} else {
console.log('Пользователь не авторизован');
$('#profileDropdown').hide();
$('#adminDropdown').hide();
}
}
// Загрузка категорий в навигацию
async loadCategoriesNav() {
try {
const categories = await this.warehouse.getCategories();
const categoriesNav = $('#categoriesNav');
// Очищаем существующие категории (кроме "Главная")
categoriesNav.find('li:not(:first-child)').remove();
// Добавляем активные категории
categories.forEach(category => {
if (category.active) {
const categoryLink = `
<li class="nav-item">
<a class="nav-link" href="catalog.html?category=${category.id}" title="${category.description || ''}">
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
${category.name}
</a>
</li>
`;
categoriesNav.append(categoryLink);
}
});
} catch (error) {
console.error('Ошибка загрузки категорий:', error);
}
}
// Загрузка категорий в выпадающее меню
async loadCategoriesDropdown() {
try {
const categories = await this.warehouse.getCategories();
const dropdown = $('#catalogCategoriesDropdown');
// Очищаем существующие категории (кроме "Все позиции" и разделителя)
dropdown.find('li:not(:first-child):not(:nth-child(2))').remove();
// Добавляем активные категории
categories.forEach(category => {
if (category.active) {
const categoryLink = `
<li>
<a class="dropdown-item" href="catalog.html?category=${category.id}" title="${category.description || ''}">
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
${category.name}
</a>
</li>
`;
dropdown.append(categoryLink);
}
});
} catch (error) {
console.error('Ошибка загрузки категорий в выпадающее меню:', error);
}
}
}
// Инициализация приложения

View File

@ -10,9 +10,36 @@ class ProfileManager {
}
init() {
$(document).ready(() => {
this.loadProfile();
this.bindEvents();
$(document).ready(async () => {
try {
// Проверяем авторизацию
if (!auth.isUserAuthenticated()) {
console.log('Пользователь не авторизован, перенаправление на login.html');
window.location.href = 'login.html';
return;
}
console.log('Инициализация профиля для пользователя:', auth.getCurrentUser()?.name);
// Загружаем профиль
await this.loadProfile();
// Загружаем категории в навигацию
await this.loadCategoriesNav();
// Загружаем категории в выпадающее меню
await this.loadCategoriesDropdown();
// Обновляем информацию о пользователе
this.updateUserInfo();
// Привязываем события
this.bindEvents();
} catch (error) {
console.error('Ошибка инициализации:', error);
auth.showNotification('Ошибка загрузки данных', 'danger');
}
});
}
@ -30,6 +57,13 @@ class ProfileManager {
e.preventDefault();
this.saveProfile();
});
// Обработчик выхода
$('#logoutBtn').on('click', (e) => {
e.preventDefault();
auth.logout();
window.location.href = 'login.html';
});
}
// Загрузка профиля
@ -427,6 +461,78 @@ class ProfileManager {
return recommendations;
}
// Обновление информации о пользователе
updateUserInfo() {
const user = auth.getCurrentUser();
if (user) {
$('#currentUserInfo').text(user.name);
$('#loginLink').hide();
$('#profileDropdown').show();
$('#adminDropdown').show();
} else {
$('#currentUserInfo').text('Гость');
$('#loginLink').show();
$('#profileDropdown').hide();
$('#adminDropdown').hide();
}
}
// Загрузка категорий в навигацию
async loadCategoriesNav() {
try {
const categories = await this.warehouse.getCategories();
const categoriesNav = $('#categoriesNav');
// Очищаем существующие категории (кроме "Главная")
categoriesNav.find('li:not(:first-child)').remove();
// Добавляем активные категории
categories.forEach(category => {
if (category.active) {
const categoryLink = `
<li class="nav-item">
<a class="nav-link" href="catalog.html?category=${category.id}" title="${category.description || ''}">
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
${category.name}
</a>
</li>
`;
categoriesNav.append(categoryLink);
}
});
} catch (error) {
console.error('Ошибка загрузки категорий:', error);
}
}
// Загрузка категорий в выпадающее меню
async loadCategoriesDropdown() {
try {
const categories = await this.warehouse.getCategories();
const dropdown = $('#catalogCategoriesDropdown');
// Очищаем существующие категории (кроме "Все позиции" и разделителя)
dropdown.find('li:not(:first-child):not(:nth-child(2))').remove();
// Добавляем активные категории
categories.forEach(category => {
if (category.active) {
const categoryLink = `
<li>
<a class="dropdown-item" href="catalog.html?category=${category.id}" title="${category.description || ''}">
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
${category.name}
</a>
</li>
`;
dropdown.append(categoryLink);
}
});
} catch (error) {
console.error('Ошибка загрузки категорий в выпадающее меню:', error);
}
}
}
// Инициализация менеджера профиля

View File

@ -16,10 +16,39 @@ class SearchManager {
}
init() {
$(document).ready(() => {
this.loadCategories();
this.bindEvents();
this.initializeFilters();
$(document).ready(async () => {
try {
// Проверяем авторизацию
if (!auth.isUserAuthenticated()) {
console.log('Пользователь не авторизован, перенаправление на login.html');
window.location.href = 'login.html';
return;
}
console.log('Инициализация поиска для пользователя:', auth.getCurrentUser()?.name);
// Загружаем категории
await this.loadCategories();
// Загружаем категории в навигацию
await this.loadCategoriesNav();
// Загружаем категории в выпадающее меню
await this.loadCategoriesDropdown();
// Обновляем информацию о пользователе
this.updateUserInfo();
// Привязываем события
this.bindEvents();
// Инициализируем фильтры
this.initializeFilters();
} catch (error) {
console.error('Ошибка инициализации:', error);
auth.showNotification('Ошибка загрузки данных', 'danger');
}
});
}
@ -45,6 +74,13 @@ class SearchManager {
e.preventDefault();
$(e.target).tab('show');
});
// Обработчик выхода
$('#logoutBtn').on('click', (e) => {
e.preventDefault();
auth.logout();
window.location.href = 'login.html';
});
}
// Загрузка категорий для фильтра
@ -113,7 +149,7 @@ class SearchManager {
this.filters.endDate = $('#endDate').val();
}
// Поиск товаров
// Поиск позиций
async searchItems(query) {
if (this.filters.searchType === 'operations') {
this.searchResults.items = [];
@ -218,7 +254,7 @@ class SearchManager {
<div class="col-12">
<div class="empty-state">
<i class="fas fa-box-open"></i>
<h5>Товары не найдены</h5>
<h5>Позиции не найдены</h5>
<p>Попробуйте изменить поисковый запрос</p>
</div>
</div>
@ -226,7 +262,7 @@ class SearchManager {
return;
}
// Пагинация для товаров
// Пагинация для позиций
const totalPages = Math.ceil(this.searchResults.items.length / this.resultsPerPage);
const startIndex = (this.currentPage - 1) * this.resultsPerPage;
const endIndex = startIndex + this.resultsPerPage;
@ -238,7 +274,7 @@ class SearchManager {
});
}
// Создание карточки товара
// Создание карточки позиции
createItemCard(item) {
const category = this.getCategoryInfo(item.category);
const stockStatus = this.getStockStatus(item);
@ -404,7 +440,7 @@ class SearchManager {
return;
}
// Сортировка товаров
// Сортировка позиций
this.searchResults.items.sort((a, b) => {
let aValue = a[sortBy];
let bValue = b[sortBy];
@ -475,7 +511,7 @@ class SearchManager {
csv += `Запрос: ${this.currentQuery}\n`;
csv += `Дата поиска: ${new Date().toLocaleDateString('ru-RU')}\n\n`;
// Экспорт товаров
// Экспорт позиций
if (this.searchResults.items.length > 0) {
csv += 'ТОВАРЫ\n';
csv += 'ID,Название,Категория,Цена,Количество,Описание\n';
@ -513,7 +549,7 @@ class SearchManager {
}
}
// Просмотр деталей товара
// Просмотр деталей позиции
async viewItemDetails(itemId) {
try {
const item = await this.warehouse.getItemById(itemId);
@ -587,8 +623,8 @@ class SearchManager {
$('#itemDetailsModal').modal('show');
} catch (error) {
console.error('Ошибка при загрузке деталей товара:', error);
auth.showNotification('Ошибка при загрузке деталей товара', 'danger');
console.error('Ошибка при загрузке деталей позиции:', error);
auth.showNotification('Ошибка при загрузке деталей позиции', 'danger');
}
}
@ -661,6 +697,78 @@ class SearchManager {
hideLoading() {
$('#loadingState').hide();
}
// Обновление информации о пользователе
updateUserInfo() {
const user = auth.getCurrentUser();
if (user) {
$('#currentUserInfo').text(user.name);
$('#loginLink').hide();
$('#profileDropdown').show();
$('#adminDropdown').show();
} else {
$('#currentUserInfo').text('Гость');
$('#loginLink').show();
$('#profileDropdown').hide();
$('#adminDropdown').hide();
}
}
// Загрузка категорий в навигацию
async loadCategoriesNav() {
try {
const categories = await this.warehouse.getCategories();
const categoriesNav = $('#categoriesNav');
// Очищаем существующие категории (кроме "Главная")
categoriesNav.find('li:not(:first-child)').remove();
// Добавляем активные категории
categories.forEach(category => {
if (category.active) {
const categoryLink = `
<li class="nav-item">
<a class="nav-link" href="catalog.html?category=${category.id}" title="${category.description || ''}">
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
${category.name}
</a>
</li>
`;
categoriesNav.append(categoryLink);
}
});
} catch (error) {
console.error('Ошибка загрузки категорий:', error);
}
}
// Загрузка категорий в выпадающее меню
async loadCategoriesDropdown() {
try {
const categories = await this.warehouse.getCategories();
const dropdown = $('#catalogCategoriesDropdown');
// Очищаем существующие категории (кроме "Все позиции" и разделителя)
dropdown.find('li:not(:first-child):not(:nth-child(2))').remove();
// Добавляем активные категории
categories.forEach(category => {
if (category.active) {
const categoryLink = `
<li>
<a class="dropdown-item" href="catalog.html?category=${category.id}" title="${category.description || ''}">
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
${category.name}
</a>
</li>
`;
dropdown.append(categoryLink);
}
});
} catch (error) {
console.error('Ошибка загрузки категорий в выпадающее меню:', error);
}
}
}
// Инициализация менеджера поиска

View File

@ -7,7 +7,7 @@ class WarehouseManager {
this.categories = this.loadCategories();
}
// Загрузка товаров из localStorage
// Загрузка позиций из localStorage
loadItems() {
const items = localStorage.getItem('warehouse_items');
return items ? JSON.parse(items) : this.getDefaultItems();
@ -21,16 +21,88 @@ class WarehouseManager {
// Загрузка категорий
loadCategories() {
const categories = localStorage.getItem('warehouse_categories');
if (categories) {
this.categories = JSON.parse(categories);
} else {
// Загружаем категории по умолчанию
this.categories = this.getDefaultCategories();
this.saveCategories();
}
// Обновляем количество позиций в каждой категории
this.updateCategoryItemCounts();
}
// Получение категорий по умолчанию
getDefaultCategories() {
return [
{ id: 'electronics', name: 'Электроника', icon: 'fas fa-laptop' },
{ id: 'clothing', name: 'Одежда', icon: 'fas fa-tshirt' },
{ id: 'tools', name: 'Инструменты', icon: 'fas fa-tools' },
{ id: 'office', name: 'Офисные принадлежности', icon: 'fas fa-briefcase' },
{ id: 'furniture', name: 'Мебель', icon: 'fas fa-couch' },
{ id: 'books', name: 'Книги', icon: 'fas fa-book' }
{
id: 'electronics',
name: 'Электроника',
description: 'Электронные компоненты и устройства',
icon: 'fas fa-microchip',
color: '#2c5aa0',
active: true,
itemCount: 0,
createdAt: new Date().toISOString()
},
{
id: 'components',
name: 'Компоненты',
description: 'Радиодетали и электронные компоненты',
icon: 'fas fa-cogs',
color: '#28a745',
active: true,
itemCount: 0,
createdAt: new Date().toISOString()
},
{
id: 'tools',
name: 'Инструменты',
description: 'Ручной и электроинструмент',
icon: 'fas fa-tools',
color: '#ffc107',
active: true,
itemCount: 0,
createdAt: new Date().toISOString()
},
{
id: 'office',
name: 'Офисные принадлежности',
description: 'Канцелярские товары и офисное оборудование',
icon: 'fas fa-briefcase',
color: '#6c757d',
active: true,
itemCount: 0,
createdAt: new Date().toISOString()
},
{
id: 'furniture',
name: 'Мебель',
description: 'Офисная мебель и оборудование',
icon: 'fas fa-chair',
color: '#8b4513',
active: true,
itemCount: 0,
createdAt: new Date().toISOString()
}
];
}
// Сохранение категорий
saveCategories() {
localStorage.setItem('warehouse_categories', JSON.stringify(this.categories));
}
// Обновление количества позиций в категориях
updateCategoryItemCounts() {
this.categories.forEach(category => {
category.itemCount = this.items.filter(item => item.category === category.id).length;
});
this.saveCategories();
}
// Сохранение данных в localStorage
saveItems() {
localStorage.setItem('warehouse_items', JSON.stringify(this.items));
@ -40,7 +112,7 @@ class WarehouseManager {
localStorage.setItem('warehouse_operations', JSON.stringify(this.operations));
}
// Получение товаров по умолчанию
// Получение позиций по умолчанию
getDefaultItems() {
return [
{
@ -54,8 +126,8 @@ class WarehouseManager {
minQuantity: 5,
location: 'Стеллаж A-1',
supplier: 'Dell Inc.',
createdAt: '2024-01-15T10:00:00Z',
updatedAt: '2024-01-15T10:00:00Z'
createdAt: '2025-01-15T10:00:00Z',
updatedAt: '2025-01-15T10:00:00Z'
},
{
id: 2,
@ -68,13 +140,13 @@ class WarehouseManager {
minQuantity: 3,
location: 'Стеллаж A-2',
supplier: 'HP Inc.',
createdAt: '2024-01-10T14:30:00Z',
updatedAt: '2024-01-10T14:30:00Z'
createdAt: '2025-01-10T14:30:00Z',
updatedAt: '2025-01-10T14:30:00Z'
},
{
id: 3,
name: 'Офисный стул',
category: 'furniture',
category: 'office',
price: 3500,
quantity: 25,
description: 'Офисный стул с подлокотниками, черный',
@ -82,8 +154,8 @@ class WarehouseManager {
minQuantity: 10,
location: 'Стеллаж B-1',
supplier: 'МебельСтрой',
createdAt: '2024-01-05T09:15:00Z',
updatedAt: '2024-01-05T09:15:00Z'
createdAt: '2025-01-05T09:15:00Z',
updatedAt: '2025-01-05T09:15:00Z'
},
{
id: 4,
@ -96,8 +168,8 @@ class WarehouseManager {
minQuantity: 20,
location: 'Стеллаж C-1',
supplier: 'ОфисМаркет',
createdAt: '2024-01-12T11:45:00Z',
updatedAt: '2024-01-12T11:45:00Z'
createdAt: '2025-01-12T11:45:00Z',
updatedAt: '2025-01-12T11:45:00Z'
},
{
id: 5,
@ -110,14 +182,14 @@ class WarehouseManager {
minQuantity: 15,
location: 'Стеллаж D-1',
supplier: 'ИнструментСтрой',
createdAt: '2024-01-08T16:20:00Z',
updatedAt: '2024-01-08T16:20:00Z'
createdAt: '2025-01-08T16:20:00Z',
updatedAt: '2025-01-08T16:20:00Z'
}
];
}
// Получение статистики
async getStatistics() {
getStatistics() {
const today = new Date().toISOString().split('T')[0];
const totalItems = this.items.reduce((sum, item) => sum + item.quantity, 0);
@ -137,17 +209,22 @@ class WarehouseManager {
};
}
// Получение всех товаров
// Получение всех позиций
async getAllItems() {
return this.items;
}
// Получение товара по ID
// Синхронное получение всех позиций
getAllItemsSync() {
return this.items;
}
// Получение позиции по ID
async getItemById(id) {
return this.items.find(item => item.id === parseInt(id));
}
// Добавление нового товара
// Добавление новой позиции
async addItem(itemData) {
const newItem = {
id: this.getNextItemId(),
@ -167,22 +244,22 @@ class WarehouseManager {
this.items.push(newItem);
this.saveItems();
// Создаем операцию прихода для нового товара
// Создаем операцию прихода для новой позиции
await this.createOperation({
type: 'incoming',
itemId: newItem.id,
quantity: newItem.quantity,
notes: 'Добавление нового товара'
notes: 'Добавление новой позиции'
});
return newItem;
}
// Обновление товара
// Обновление позиции
async updateItem(id, itemData) {
const itemIndex = this.items.findIndex(item => item.id === parseInt(id));
if (itemIndex === -1) {
throw new Error('Товар не найден');
throw new Error('Позиция не найдена');
}
this.items[itemIndex] = {
@ -195,11 +272,11 @@ class WarehouseManager {
return this.items[itemIndex];
}
// Удаление товара
// Удаление позиции
async deleteItem(id) {
const itemIndex = this.items.findIndex(item => item.id === parseInt(id));
if (itemIndex === -1) {
throw new Error('Товар не найден');
throw new Error('Позиция не найдена');
}
this.items.splice(itemIndex, 1);
@ -214,10 +291,10 @@ class WarehouseManager {
const item = await this.getItemById(itemId);
if (!item) {
throw new Error('Товар не найден');
throw new Error('Позиция не найдена');
}
// Обновляем количество товара
// Обновляем количество позиции
item.quantity += quantity;
item.updatedAt = new Date().toISOString();
this.saveItems();
@ -241,15 +318,15 @@ class WarehouseManager {
const item = await this.getItemById(itemId);
if (!item) {
throw new Error('Товар не найден');
throw new Error('Позиция не найдена');
}
// Проверяем достаточность товара
// Проверяем достаточность позиции
if (item.quantity < quantity) {
throw new Error(`Недостаточно товара. Доступно: ${item.quantity} шт.`);
throw new Error(`Недостаточно позиций. Доступно: ${item.quantity} шт.`);
}
// Обновляем количество товара
// Обновляем количество позиции
item.quantity -= quantity;
item.updatedAt = new Date().toISOString();
this.saveItems();
@ -401,18 +478,63 @@ class WarehouseManager {
return this.categories;
}
// Получение категории по ID
async getCategoryById(id) {
return this.categories.find(cat => cat.id === id);
}
// Добавление категории
async addCategory(categoryData) {
const newCategory = {
id: categoryData.id || this.generateCategoryId(),
name: categoryData.name,
icon: categoryData.icon || 'fas fa-box'
id: this.generateCategoryId(),
...categoryData,
itemCount: 0,
createdAt: new Date().toISOString()
};
this.categories.push(newCategory);
this.saveCategories();
return newCategory;
}
// Обновление категории
async updateCategory(id, categoryData) {
const categoryIndex = this.categories.findIndex(cat => cat.id === id);
if (categoryIndex === -1) {
throw new Error('Категория не найдена');
}
this.categories[categoryIndex] = {
...this.categories[categoryIndex],
...categoryData,
updatedAt: new Date().toISOString()
};
this.saveCategories();
return this.categories[categoryIndex];
}
// Удаление категории
async deleteCategory(id) {
const categoryIndex = this.categories.findIndex(cat => cat.id === id);
if (categoryIndex === -1) {
throw new Error('Категория не найдена');
}
// Удаляем все позиции этой категории
this.items = this.items.filter(item => item.category !== id);
this.saveItems();
// Удаляем категорию
this.categories.splice(categoryIndex, 1);
this.saveCategories();
// Обновляем количество позиций в остальных категориях
this.updateCategoryItemCounts();
return true;
}
// Генерация следующего ID для товара
getNextItemId() {
const maxId = Math.max(...this.items.map(item => item.id), 0);