Add files via upload
v0.0.3
This commit is contained in:
41
js/auth.js
41
js/auth.js
@ -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;
|
||||
}
|
||||
|
||||
// Получение списка пользователей (имитация базы данных)
|
||||
|
||||
276
js/catalog.js
276
js/catalog.js
@ -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
403
js/categories.js
Normal 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';
|
||||
});
|
||||
});
|
||||
126
js/history.js
126
js/history.js
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Инициализация менеджера истории
|
||||
|
||||
149
js/main.js
149
js/main.js
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Инициализация приложения
|
||||
|
||||
112
js/profile.js
112
js/profile.js
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Инициализация менеджера профиля
|
||||
|
||||
134
js/search.js
134
js/search.js
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Инициализация менеджера поиска
|
||||
|
||||
198
js/warehouse.js
198
js/warehouse.js
@ -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);
|
||||
|
||||
Reference in New Issue
Block a user