diff --git a/api/routers/__init__.py b/api/routers/__init__.py
index 3f4d6f5..141bcbf 100644
--- a/api/routers/__init__.py
+++ b/api/routers/__init__.py
@@ -114,6 +114,9 @@ async def post_requests(
if isinstance(endDate, str):
endDate = datetime.strptime(endDate, "%Y-%m-%d").date()
+ if startDate > endDate:
+ startDate, endDate = endDate, startDate
+
jurnal_toolkits = await StocksRecordsHandler.getLogs(startDate, endDate)
if isinstance(jurnal_toolkits, list):
if len(jurnal_toolkits) == 0:
@@ -143,10 +146,42 @@ async def post_requests(
"endDate": endDate.strftime("%Y-%m-%d"),
}
case "jurnal_service":
- jurnal_service = await ServiceRecordsHandler.get()
- if jurnal_service:
- resultData["status"] = "ok"
- resultData["data"] = jurnal_service
+ startDate = request_data.get("body").get(
+ "startDate", date.today() - timedelta(days=7)
+ )
+ if isinstance(startDate, str):
+ startDate = datetime.strptime(startDate, "%Y-%m-%d").date()
+
+ endDate = request_data.get("body").get("endDate", date.today())
+ if isinstance(endDate, str):
+ endDate = datetime.strptime(endDate, "%Y-%m-%d").date()
+
+ if startDate > endDate:
+ startDate, endDate = endDate, startDate
+
+ jurnal_service = await ServiceRecordsHandler.getLogs(startDate, endDate)
+ if isinstance(jurnal_service, list):
+ if len(jurnal_service) == 0:
+ resultData["status"] = "ok"
+ resultData["data"] = {
+ "requests": [],
+ "users": [],
+ "categories": [],
+ "startDate": startDate.strftime("%Y-%m-%d"),
+ "endDate": endDate.strftime("%Y-%m-%d"),
+ }
+ else:
+ users = await UserHandler.getAll()
+ categories = await CategoryHandler.getAll()
+ resultData["status"] = "ok"
+ resultData["data"] = {
+ "requests": jurnal_service,
+ "users": users,
+ "categories": categories,
+ "startDate": startDate.strftime("%Y-%m-%d"),
+ "endDate": endDate.strftime("%Y-%m-%d"),
+ }
+ # logger.info(resultData.get("data"))
case "users":
users = await UserHandler.getAll()
if users:
diff --git a/api/routers/__pycache__/__init__.cpython-313.pyc b/api/routers/__pycache__/__init__.cpython-313.pyc
index 8fe3f59..8f8d2ea 100644
Binary files a/api/routers/__pycache__/__init__.cpython-313.pyc and b/api/routers/__pycache__/__init__.cpython-313.pyc differ
diff --git a/api/routers/__pycache__/toolkit.cpython-313.pyc b/api/routers/__pycache__/toolkit.cpython-313.pyc
index 775dbcb..67dc3d1 100644
Binary files a/api/routers/__pycache__/toolkit.cpython-313.pyc and b/api/routers/__pycache__/toolkit.cpython-313.pyc differ
diff --git a/api/routers/toolkit.py b/api/routers/toolkit.py
index 7185226..9ba3b3b 100644
--- a/api/routers/toolkit.py
+++ b/api/routers/toolkit.py
@@ -160,7 +160,7 @@ async def manage_toolkit(reqData: dict = Depends(requestDict)):
logger.info(f"Управление инструментами")
response = {"status": "error"}
action = reqData.get("body").get("action")
- userId = reqData.get("body").get("UserId")
+ userId = reqData.get("body").get("userId")
toolkitData = reqData.get("body").get("formData")
if "category_id" in toolkitData:
toolkitData["category_id"] = int(toolkitData.get("category_id"))
diff --git a/api/static/js/index.js b/api/static/js/index.js
index 328c724..ce3770b 100644
--- a/api/static/js/index.js
+++ b/api/static/js/index.js
@@ -264,7 +264,7 @@ function fillTab(tabId, tabData) {
renderJurnalToolkitsTab(tabId, tabData);
break;
case 'jurnal_service':
- renderSimpleTab(tabId, tabData, 'Сервисный журнал');
+ renderJurnalServicesTab(tabId, tabData);
break;
case 'users':
renderSimpleTab(tabId, tabData, 'Пользователи системы');
@@ -1454,10 +1454,16 @@ function renderToolkitCards(tabId, tools, categoriesMap, filterText = '', catego
}).join('');
const cards = container.querySelectorAll('.toolkit-card');
+ let activeModal = null;
+
cards.forEach(card => {
card.addEventListener('click', async event => {
+ if (activeModal) return;
+
const toolId = event.currentTarget.dataset.toolid;
+ activeModal = true;
await showToolkitDetailsModal(toolId);
+ activeModal = null;
});
});
}
@@ -3503,11 +3509,15 @@ async function initializeToolboxTable(data, toolboxOwn, quantityMonitoring) {
});
// Добавляем обработчики для изображений
+ let activeModal = null;
document.querySelectorAll('.toolkit-image-link').forEach(link => {
link.addEventListener('click', async (e) => {
+ if (activeModal) return;
e.preventDefault();
const itemId = e.currentTarget.dataset.id;
+ activeModal = true;
await showToolkitDetailsModal(itemId);
+ activeModal = null;
});
});
}
@@ -5371,7 +5381,7 @@ function renderRequestsTab(tabId, tabData) {
|
${request.action}
- ${request.created_at}
+ ${request.created_at}
|
${userMap[request.init_user_id] || `Пользователь ${request.init_user_id}`} |
${request.source_toolbox_id ? (toolboxMap[request.source_toolbox_id] || `Склад ${request.source_toolbox_id}`) : '-'} |
@@ -5851,8 +5861,8 @@ function renderJurnalToolkitsTab(tabId, tabData) {
${request.action}
${statusBadge}
- ${userMap[request.init_user_id] || `Пользователь ${request.init_user_id}`} ${request.created_at} |
- ${userMap[request.decision_user_id] || `Пользователь ${request.decision_user_id}`} ${request.decided_at} |
+ ${userMap[request.init_user_id] || `Пользователь ${request.init_user_id}`} ${request.created_at} |
+ ${userMap[request.decision_user_id] || `Пользователь ${request.decision_user_id}`} ${request.decided_at} |
${request.source_toolbox_id ? (toolboxMap[request.source_toolbox_id] || `Склад ${request.source_toolbox_id}`) : '-'} |
${request.target_toolbox_id ? (toolboxMap[request.target_toolbox_id] || `Склад ${request.target_toolbox_id}`) : '-'} |
${request.toolkit_id ? (toolkitMap[request.toolkit_id] || `Инструмент ${request.toolkit_id}`) : '-'} |
@@ -5889,6 +5899,334 @@ function renderJurnalToolkitsTab(tabId, tabData) {
// Первоначальный рендеринг таблицы
renderRequestsTable();
}
+
+function renderJurnalServicesTab(tabId, tabData) {
+ const tabContent = document.getElementById(`${tabId}-tab-content`);
+ const tabOptionalContent = document.getElementById(`${tabId}-tab-optional-content`);
+
+ const { requests, users, categories, startDate, endDate } = tabData;
+
+ if (requests.length === 0) {
+ tabContent.innerHTML = `
+
+
+ Нет данных за период ${startDate} - ${endDate}
+
+ `;
+ return;
+ }
+
+ // Собираем списки для фильтров
+ const initUsers = [...new Set(requests.map(r => r.user_id))].filter(id => id !== null);
+ const userMap = {};
+ users.forEach(user => {
+ userMap[user.id] = user.username;
+ });
+
+ const categoriesMap = {};
+ categories.forEach(cat => {
+ categoriesMap[cat.id] = { title: cat.title, description: cat.description };
+ });
+
+ // Собираем типы действий (ключи из details)
+ const actionTypes = [...new Set(requests.map(r => {
+ const details = r.details;
+ return Object.keys(details)[0]; // Берем первый ключ как тип действия
+ }))];
+
+ const savedFilters = loadFromStorage(tabId);
+ // Фильтры
+ let currentFilters = {
+ user: savedFilters?.user || 'all',
+ action: savedFilters?.action || 'all'
+ };
+
+ // Рендерим дополнительный контейнер с фильтрами
+ tabOptionalContent.innerHTML = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Дата начала:
+
+
+
+
+
+ Дата окончания:
+
+
+
+
+
+
+
+
+
+ `;
+
+ const filterResetBtn = document.getElementById(`${tabId}-filter-reset-btn`);
+ filterResetBtn.addEventListener('click', () => {
+ currentFilters = {
+ user: 'all',
+ action: 'all'
+ };
+ document.getElementById(`${tabId}-user-filter`).value = currentFilters.user;
+ document.getElementById(`${tabId}-action-filter`).value = currentFilters.action;
+ saveToStorage(tabId, currentFilters);
+ renderServicesTable();
+ });
+
+ const startDateInput = document.getElementById(`${tabId}-date-from`);
+ const endDateInput = document.getElementById(`${tabId}-date-to`);
+ startDateInput.value = startDate;
+ endDateInput.value = endDate;
+
+ const refreshDateBtn = document.getElementById(`${tabId}-date-update-btn`);
+ refreshDateBtn.addEventListener('click', async () => {
+ const newStartDate = startDateInput.value;
+ const newEndDate = endDateInput.value;
+ const newDateRequestData = {
+ tabId: tabId,
+ startDate: newStartDate,
+ endDate: newEndDate
+ };
+ if (newStartDate && newEndDate) {
+ tabContent.innerHTML = `
+
+
+ Загрузка данных...
+
+ `;
+ const newPeriodData = await apiRequest('/', newDateRequestData);
+ if (newPeriodData.status == 'ok') {
+ renderJurnalServicesTab(tabId, newPeriodData.data);
+ }
+ }
+ });
+
+ // Рендерим основной контейнер с таблицей сервисных событий
+ tabContent.innerHTML = `
+
+
+
+
+
+
+ | Дата |
+ Пользователь |
+ Действие |
+ Детали |
+
+
+
+
+
+
+
+
+
+
Нет событий по выбранным фильтрам
+
+
+
+ `;
+
+ // Функция для фильтрации событий
+ function filterServices() {
+ let filtered = requests;
+
+ // Фильтр по пользователю
+ if (currentFilters.user !== 'all') {
+ if (currentFilters.user === 'system') {
+ filtered = filtered.filter(r => r.user_id === null);
+ } else {
+ filtered = filtered.filter(r => r.user_id == currentFilters.user);
+ }
+ }
+
+ // Фильтр по типу действия
+ if (currentFilters.action !== 'all') {
+ filtered = filtered.filter(r => {
+ const details = r.details;
+ return Object.keys(details)[0] === currentFilters.action;
+ });
+ }
+
+ return filtered;
+ }
+
+ // Функция для рендеринга строк таблицы
+ function renderServicesTable() {
+ const tbody = document.getElementById(`${tabId}-services-body`);
+ const noServicesDiv = document.getElementById(`${tabId}-no-services`);
+ const filteredServices = filterServices();
+
+ if (filteredServices.length === 0) {
+ tbody.innerHTML = '';
+ noServicesDiv.style.display = 'block';
+ return;
+ }
+
+ noServicesDiv.style.display = 'none';
+
+ tbody.innerHTML = filteredServices.map(service => {
+ const actionType = Object.keys(service.details)[0];
+ const actionData = service.details[actionType];
+
+ // Определяем пользователя
+ let userName = 'Система';
+ if (service.user_id) {
+ userName = userMap[service.user_id] || `Пользователь ${service.user_id}`;
+ }
+
+ // Форматируем детали в зависимости от типа действия
+ let detailsHtml = '';
+
+ if (actionType === 'Авторизован пользователь') {
+ // Для авторизации
+ detailsHtml = `
+ ${actionData}
+ `;
+ } else if (actionType.includes('Добавлен') || actionType.includes('Обновлен') || actionType.includes('Добавлена')) {
+ // Для добавления/обновления сущностей
+ const entityName = actionData.title || actionData.username || actionData.login || '';
+ detailsHtml = `
+ ${entityName}
+ ${actionData.description ? `${actionData.description}
` : ''}
+ ${actionData.id ? `ID: ${actionData.id}
` : ''}
+ `;
+
+ // Для инструментов добавляем дополнительные поля
+ if (actionData.specifications && Object.keys(actionData.specifications).length > 0) {
+ detailsHtml += `Характеристики:
`;
+ detailsHtml += ``;
+ for (const [key, value] of Object.entries(actionData.specifications)) {
+ detailsHtml += `
${key}: ${value}
`;
+ }
+ detailsHtml += `
`;
+ }
+
+ if (actionData.external_link) {
+ detailsHtml += ``;
+ }
+
+ if (actionData.quantity_min || actionData.quantity_min_extra) {
+ detailsHtml += `Мониторинг остатков:
`;
+ if (actionData.quantity_min && actionData.quantity_min_extra) {
+ detailsHtml += `Минимальное количество: ${actionData.quantity_min}
`;
+ detailsHtml += `Минимальное критическое количество: ${actionData.quantity_min_extra}
`;
+ } else if (actionData.quantity_min && !actionData.quantity_min_extra) {
+ detailsHtml += `Минимальное количество: ${actionData.quantity_min}
`;
+ } else if (actionData.quantity_min_extra && !actionData.quantity_min) {
+ detailsHtml += `Минимальное критическое количество: ${actionData.quantity_min_extra}
`;
+ }
+ }
+
+ if (actionData.category_id) {
+ detailsHtml += `Категория: ${categoriesMap[actionData.category_id].title} [${categoriesMap[actionData.category_id].description}]
`;
+ }
+
+ if (actionData.image) {
+ detailsHtml += `Изображения:
`;
+ detailsHtml += `Основное:
`;
+ detailsHtml += `

`;
+ if (actionData.image.additional) {
+ detailsHtml += `
Дополнительные:
`;
+ detailsHtml += `
`;
+ actionData.image.additional.forEach(img => {
+ detailsHtml += `
`;
+ });
+ detailsHtml += `
`;
+ }
+ detailsHtml += `
`;
+ }
+ }
+
+ return `
+
+ |
+ ${service.created_at}
+ |
+ ${userName} |
+
+ ${actionType}
+ |
+
+ ${detailsHtml}
+ |
+
+ `;
+ }).join('');
+ }
+
+ // Назначаем обработчики событий для фильтров
+ document.getElementById(`${tabId}-user-filter`).addEventListener('change', function () {
+ currentFilters.user = this.value;
+ saveToStorage(tabId, currentFilters);
+ renderServicesTable();
+ });
+
+ document.getElementById(`${tabId}-action-filter`).addEventListener('change', function () {
+ currentFilters.action = this.value;
+ saveToStorage(tabId, currentFilters);
+ renderServicesTable();
+ });
+
+ // Устанавливаем сохраненные значения фильтров
+ if (savedFilters) {
+ document.getElementById(`${tabId}-user-filter`).value = currentFilters.user;
+ document.getElementById(`${tabId}-action-filter`).value = currentFilters.action;
+ }
+
+ // Первоначальный рендеринг таблицы
+ renderServicesTable();
+}
+
document.addEventListener('DOMContentLoaded', async () => {
await getCookieData();
diff --git a/db/handlers/__pycache__/records.cpython-313.pyc b/db/handlers/__pycache__/records.cpython-313.pyc
index dd10b1e..efdd21b 100644
Binary files a/db/handlers/__pycache__/records.cpython-313.pyc and b/db/handlers/__pycache__/records.cpython-313.pyc differ
diff --git a/db/handlers/__pycache__/toolkit.cpython-313.pyc b/db/handlers/__pycache__/toolkit.cpython-313.pyc
index 047be3c..01a5e17 100644
Binary files a/db/handlers/__pycache__/toolkit.cpython-313.pyc and b/db/handlers/__pycache__/toolkit.cpython-313.pyc differ
diff --git a/db/handlers/records.py b/db/handlers/records.py
index a4129e4..190836f 100644
--- a/db/handlers/records.py
+++ b/db/handlers/records.py
@@ -287,3 +287,35 @@ class ServiceRecordsHandler:
except Exception as e:
logger.error(f"Ошибка получения записей: {str(e)}")
return False
+
+ async def getLogs(startDate: date, endDate: date):
+ from db import CRUD
+
+ try:
+ start_dt = datetime.combine(startDate, time.min)
+ end_dt = datetime.combine(endDate, time.max)
+
+ query = (
+ select(ServicesRecords)
+ .where(
+ ServicesRecords.created_at.between(start_dt, end_dt),
+ )
+ .order_by(ServicesRecords.created_at.desc())
+ )
+
+ logger.debug("Получение записей за период %s - %s", startDate, endDate)
+
+ records = await CRUD.read(query, True)
+
+ logger.debug(
+ "%d записей за период %s - %s успешно получены",
+ len(records),
+ startDate,
+ endDate,
+ )
+
+ return [record.toDict() for record in records]
+
+ except Exception:
+ logger.exception("Ошибка получения записей")
+ return []
diff --git a/db/handlers/toolkit.py b/db/handlers/toolkit.py
index ab5b8cd..56b71b1 100644
--- a/db/handlers/toolkit.py
+++ b/db/handlers/toolkit.py
@@ -160,7 +160,7 @@ class ToolkitHandler:
f"Инструмент {editedToolkit.title} успешно обновлен, изменены данные: {kwargs.keys()}"
)
await ServiceRecordsHandler.add(
- user_id, {f"Обновлен инструмент {toolkit.title}": editedToolkit.toDict()}
+ user_id, {f"Обновлен инструмент": editedToolkit.toDict()}
)
return editedToolkit.toDict()