From a9e57b4a386fd5c5ab60fad8e7804f7f657b212f Mon Sep 17 00:00:00 2001 From: Macbook Date: Sat, 13 Dec 2025 23:47:46 +0300 Subject: [PATCH] =?UTF-8?q?=D0=96=D1=83=D1=80=D0=BD=D0=B0=D0=BB=20=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=BC=D0=B5=D1=89=D0=B5=D0=BD=D0=B8=D0=B9,?= =?UTF-8?q?=20=D1=83=D0=BF=D1=80=D0=BE=D1=89=D0=B5=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=B8=20=D1=87=D0=B8=D1=81=D1=82=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/routers/__init__.py | 74 +- .../__pycache__/__init__.cpython-313.pyc | Bin 5100 -> 8030 bytes .../__pycache__/records.cpython-313.pyc | Bin 0 -> 1367 bytes .../__pycache__/stocks.cpython-313.pyc | Bin 5783 -> 5811 bytes .../__pycache__/toolbox.cpython-313.pyc | Bin 5012 -> 4869 bytes .../__pycache__/toolkit.cpython-313.pyc | Bin 11467 -> 11467 bytes api/routers/records.py | 21 + api/routers/stocks.py | 2 +- api/routers/toolbox.py | 2 - api/routers/toolkit.py | 1 - api/static/js/index.js | 872 ++++++++++++++++-- api/static/js/user.js | 3 + .../__pycache__/access.cpython-313.pyc | Bin 8915 -> 8801 bytes .../__pycache__/actions.cpython-313.pyc | Bin 26312 -> 26390 bytes .../__pycache__/records.cpython-313.pyc | Bin 15078 -> 16486 bytes db/handlers/access.py | 5 - db/handlers/actions.py | 4 +- db/handlers/records.py | 74 +- db/schemas/__pycache__/access.cpython-313.pyc | Bin 2922 -> 2733 bytes db/schemas/access.py | 4 - 20 files changed, 961 insertions(+), 101 deletions(-) create mode 100644 api/routers/__pycache__/records.cpython-313.pyc create mode 100644 api/routers/records.py diff --git a/api/routers/__init__.py b/api/routers/__init__.py index 29ecfc6..3f4d6f5 100644 --- a/api/routers/__init__.py +++ b/api/routers/__init__.py @@ -1,3 +1,4 @@ +from datetime import date, datetime, timedelta from fastapi import APIRouter, Depends, Request from fastapi.responses import RedirectResponse @@ -7,6 +8,7 @@ from .user import router as user from .stocks import router as stocks from .toolbox import router as toolbox from .toolkit import router as toolkit +from .records import router as records router = APIRouter() @@ -15,6 +17,7 @@ router.include_router(user, prefix="/user", tags=["user"]) router.include_router(stocks, prefix="/stocks", tags=["stocks"]) router.include_router(toolbox, prefix="/toolbox", tags=["toolbox"]) router.include_router(toolkit, prefix="/toolkit", tags=["toolkit"]) +router.include_router(records, prefix="/records", tags=["records"]) @router.get("/") @@ -50,9 +53,16 @@ async def post_requests( reqData = { "tab": request_data.get("body").get("tabId"), - "userData": request_data.get("body").get("cookiesData").get("userData"), - "accessData": request_data.get("body").get("cookiesData").get("accessData"), } + if "cookiesData" in request_data.get("body"): + if "userData" in request_data.get("body").get("cookiesData"): + reqData["userData"] = ( + request_data.get("body").get("cookiesData").get("userData") + ) + if "accessData" in request_data.get("body").get("cookiesData"): + reqData["accessData"] = ( + request_data.get("body").get("cookiesData").get("accessData") + ) resultData = {"status": "error", "data": {}} logger.info(f"Получение данных для вкладки {reqData.get('tab')}") match reqData.get("tab"): @@ -67,10 +77,23 @@ async def post_requests( resultData["status"] = "ok" resultData["data"] = toolbox case "requests": - requests = await StocksRecordsHandler.get(reqData.get("userData").get("id")) + canDesign = reqData.get("accessData").get( + "refund_request_confirm", False + ) or reqData.get("accessData").get("debit_request_confirm", False) + userId = reqData.get("userData").get("id") + requests = await StocksRecordsHandler.get(userId, canDesign) + users = await UserHandler.getAll() + toolboxes = await ToolboxHandler.getAll() + toolkitsIds = set([request.get("toolkit_id") for request in requests]) + toolkits = await ToolkitHandler.getSeveral(list(toolkitsIds)) if isinstance(requests, list): resultData["status"] = "ok" - resultData["data"] = requests + resultData["data"] = { + "requests": requests, + "users": users, + "toolboxes": toolboxes, + "toolkits": toolkits, + } case "toolkits": toolkits = await ToolkitHandler.getAll() categories = await CategoryHandler.getAll() @@ -80,12 +103,45 @@ async def post_requests( "toolkits": toolkits, "categories": categories, } - # logger.info(resultData) case "jurnal_toolkits": - jurnal_toolkits = await StocksRecordsHandler.get() - if jurnal_toolkits: - resultData["status"] = "ok" - resultData["data"] = jurnal_toolkits + 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() + + jurnal_toolkits = await StocksRecordsHandler.getLogs(startDate, endDate) + if isinstance(jurnal_toolkits, list): + if len(jurnal_toolkits) == 0: + resultData["status"] = "ok" + resultData["data"] = { + "requests": [], + "users": [], + "toolboxes": [], + "toolkits": [], + "startDate": startDate.strftime("%Y-%m-%d"), + "endDate": endDate.strftime("%Y-%m-%d"), + } + else: + users = await UserHandler.getAll() + toolboxes = await ToolboxHandler.getAll() + toolkitsIds = set( + [request.get("toolkit_id") for request in jurnal_toolkits] + ) + toolkits = await ToolkitHandler.getSeveral(list(toolkitsIds)) + resultData["status"] = "ok" + resultData["data"] = { + "requests": jurnal_toolkits, + "users": users, + "toolboxes": toolboxes, + "toolkits": toolkits, + "startDate": startDate.strftime("%Y-%m-%d"), + "endDate": endDate.strftime("%Y-%m-%d"), + } case "jurnal_service": jurnal_service = await ServiceRecordsHandler.get() if jurnal_service: diff --git a/api/routers/__pycache__/__init__.cpython-313.pyc b/api/routers/__pycache__/__init__.cpython-313.pyc index 61f5e61121a48c4b0711e4b0e5e96c5f56fe7fd7..8fe3f590bec47b3f7633cba74a560d2d95cd1ccb 100644 GIT binary patch literal 8030 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 literal 0 HcmV?d00001 diff --git a/api/routers/__pycache__/stocks.cpython-313.pyc b/api/routers/__pycache__/stocks.cpython-313.pyc index d2f120b81657707b917ddb8edc3fa5396dc9c027..a20337c42ec13fb72841b15c46e0a4d00e136b23 100644 GIT binary patch delta 368 zcmbQPyIGg_GcPX}0}!N~w$0?($m_@?p~A$#pv<7aFqI*i5hMcwp$tLHlLa|t89}7* zR6oE+ufmJd^3vd3$JeP@$`xbL%UdiMsY)1@@fl5FI7Bd5# z^OB)~;R^?YpvVND>mn)_MN}4qT^7-~E@E&|#9)K;Wf9xU0(O&I*q@myx(F~HRAzMH zVmYYd$Ohyb(qeSsVL7Dj$Oz;d)?swvV>zrV2V@`N0f`*p1CpEPbIf69RG(}kkS44S ww6cf?L}-8rH6Ss0t3WEF>11g^4MwZUE`nZcAg2`>Z=NFP!op}g`KIVT03>-!9{>OV delta 268 zcmdn2J6)IeGcPX}0}%KyTV}FvqCa-2YqGJTq$p^&6 z|A53xh6aWY91Mc8*98nQSho!Du--LC}j0b31lDPHFss*%qmpFC@jS&@|l@|$&v9R6OdWt H08|eEksvkS delta 305 zcmZowo1)J9nU|M~0SGG3TV_7n$or0)iHUjgUv87hhD-`9p$tK+let)B_)Hl=Y8V(4 z7=oFDStchkNl%t#<(wSJV-BWgLg||jdb1?2E+ao114A%>C}S|QI#8p(=;UxdEs&u) zp^OOALK#&UI2imQ%$d^|HAVc2G=NHr1V98Ih~VG6kgtGID;{Y3ONIu9CoEj;-c8;Y zgbc5+7~g?#Ca_QEp2$7J?XrOK6&97rY62fwFL6kHV4JKgsAcwrje(ULq-6rf6&A@G z+`dc{-B!GcPX}0}yB*wavV;kvEe~@`j>nhsT77i#&2Si_@% delta 136 zcmX>dc{-B!GcPX}0}w38vdR3ukvEe~@`0jihsT77i#&2S manageToolkit()); } else { tabOptionalContent.innerHTML = ` -
-
-
+
+ +
+
+ ${categoriesArray.map(category => ` + + `).join('')} + +
+
+ + +
+
- +
@@ -1617,18 +1648,18 @@ function setupFilters(tabId, tools, categoriesMap, specData) { render(); } -function loadFromStorage(tabId) { +function loadFromStorage(title) { try { - return JSON.parse(localStorage.getItem(`toolboxStotage:${tabId}`)) || {}; + return JSON.parse(localStorage.getItem(`toolboxStotage:${title}`)) || {}; } catch { return {}; } } -function saveToStorage(tabId, storageData) { +function saveToStorage(title, data) { localStorage.setItem( - `toolboxStotage:${tabId}`, - JSON.stringify(storageData) + `toolboxStotage:${title}`, + JSON.stringify(data) ); } @@ -1948,12 +1979,11 @@ function renderToolboxTab(tabData) { // Сортируем список складов по названию tabData.sort((a, b) => a.title.localeCompare(b.title, 'ru')); - const toolboxNav = ` + tabOptionalContent.innerHTML = `
${tabData.map((toolbox, index) => ` -
`; + const toolboxNav = document.getElementById('toolboxNav'); + toolboxNav.addEventListener('click', async (event) => { + const button = event.target.closest('.toolbox-nav-btn'); + if (!button) return; + if (button.dataset.toolboxId) { + try { + await selectToolbox(button.dataset.toolboxId); + } catch (err) { + console.error('Ошибка выбора склада:', err); + } + } + }); + // Создаем контейнер для содержимого склада - const toolboxContent = ` + tabContent.innerHTML = `
@@ -1979,12 +2022,10 @@ function renderToolboxTab(tabData) {
`; - tabOptionalContent.innerHTML = toolboxNav; - if (accessData.manage_toolboxes) { const addToolboxBtn = document.createElement('button'); - addToolboxBtn.className = 'btn btn-outline-success toolbox-nav-btn d-flex align-items-center mb-2'; + addToolboxBtn.className = 'btn btn-outline-success toolbox-nav-btn'; addToolboxBtn.innerHTML = ` Добавить @@ -1996,33 +2037,41 @@ function renderToolboxTab(tabData) { document.getElementById('toolboxNav').appendChild(addToolboxBtn); } - tabContent.innerHTML = toolboxContent; - const choiceToolbox = loadFromStorage('toolbox'); if (choiceToolbox.toolboxId) { - window.selectToolbox(choiceToolbox.toolboxId).then(() => { }).catch(() => { }); + (async () => { + await selectToolbox(choiceToolbox.toolboxId); + })().catch(err => { + console.error('Ошибка выбора склада:', err); + }); } } - // Функция для выбора склада -window.selectToolbox = async function (toolboxId, index) { - // Убираем активный класс со всех кнопок складов - document.querySelectorAll('.toolbox-nav-btn').forEach(btn => { - btn.classList.remove('active'); - }); - - // Добавляем активный класс выбранной кнопке - const selectedBtn = document.querySelector(`.toolbox-nav-btn[data-toolbox-id="${toolboxId}"]`); - if (selectedBtn) { - selectedBtn.classList.add('active'); +async function selectToolbox(toolboxId) { + if (typeof toolboxId === 'string') { + try { + toolboxId = parseInt(toolboxId); + } catch (err) { + console.error('Неверный идентификатор склада:', toolboxId); + return; + } } + document.querySelectorAll('.toolbox-nav-btn') + .forEach(btn => btn.classList.remove('active')); + + const selectedBtn = document.querySelector( + `.toolbox-nav-btn[data-toolbox-id="${toolboxId}"]` + ); + + selectedBtn?.classList.add('active'); saveToStorage('toolbox', { toolboxId }); - // Загружаем содержимое склада + await loadToolboxContent(toolboxId); } async function loadToolboxContent(toolboxId) { + const contentContainer = document.querySelector('.toolbox-content-container'); // Показываем индикатор загрузки @@ -2214,12 +2263,15 @@ async function loadToolboxContent(toolboxId) { Не удалось загрузить содержимое склада
${error.message}

-
`; + + // Добавляем обработчик для кнопки "Попробовать снова" + contentContainer.querySelector('#tryAgainBtn').addEventListener('click', async () => await selectToolbox(toolboxId)); } } @@ -3326,7 +3378,6 @@ async function initializeToolboxTable(data, toolboxOwn, quantityMonitoring) { async function initializePagination() { const totalPages = Math.ceil(filteredData.length / itemsPerPage); const paginationContainer = document.getElementById('toolboxPagination'); - const tbody = document.getElementById('toolboxItemsBody'); // Очищаем текущее содержимое paginationContainer.innerHTML = ''; @@ -3443,6 +3494,7 @@ async function initializeToolboxTable(data, toolboxOwn, quantityMonitoring) { const itemId = e.currentTarget.dataset.id; const selectedItem = data.find(d => d.id == itemId); if (selectedItem) { + selectedItem.skipRefresh = true; await showOperationModal(action, selectedItem); } }); @@ -5107,26 +5159,736 @@ async function actionRequest(operation, quantity, comment, selectedItem) { } } -function formatKey(key) { - const keyMap = { - 'id': 'ID', - 'title': 'Название', - 'description': 'Описание', - 'owner_id': 'ID владельца', - 'monitoring': 'Мониторинг', - 'created_at': 'Дата создания', - 'updated_at': 'Дата обновления' +function renderRequestsTab(tabId, tabData) { + const tabContent = document.getElementById(`${tabId}-tab-content`); + const tabOptionalContent = document.getElementById(`${tabId}-tab-optional-content`); + + const { requests, users, toolboxes, toolkits } = tabData; + + // Собираем списки для фильтров + const initUsers = [...new Set(requests.map(r => r.init_user_id))]; + const userMap = {}; + users.forEach(user => { + userMap[user.id] = user.username; + }); + + const actionTypes = [...new Set(requests.map(r => r.action))]; + + const ownRequestsCount = requests.filter(r => r.init_user_id === userData.id).length; + + // Создаем мапу для toolboxes + const toolboxMap = {}; + toolboxes.forEach(box => { + toolboxMap[box.id] = box.title; + }); + + // Создаем мапу для toolkits + const toolkitMap = {}; + toolkits.forEach(kit => { + toolkitMap[kit.id] = kit.title; + }); + + // Фильтры + let currentFilters = { + user: 'all', + action: 'all', }; - return keyMap[key] || key.charAt(0).toUpperCase() + key.slice(1).replace(/_/g, ' '); + + // Рендерим дополнительный контейнер с фильтрами + tabOptionalContent.innerHTML = ` +
+ +
+
+
+
+ + + + +
+
+
+
+ + + + +
+
+
+
+ + +
+
+ ${(accessData.refund_request_confirm || accessData.debit_request_confirm) ? ` +
+ + +
+ ` : ''} + ${ownRequestsCount > 0 ? ` + + ` : ''} +
+
+
+ `; + + // Рендерим основной контейнер с таблицей запросов + tabContent.innerHTML = ` +
+
+
+ + + + + + + + + + + + + + + + +
ТипОформилСо складаНа складИнструментКол-воОбоснованиеДействия
+
+ +
+
+ `; + + // Функция для фильтрации запросов + function filterRequests() { + let filtered = requests; + + // Фильтр по пользователю + if (currentFilters.user !== 'all') { + filtered = filtered.filter(r => r.init_user_id == currentFilters.user); + } + + // Фильтр по типу действия + if (currentFilters.action !== 'all') { + filtered = filtered.filter(r => r.action === currentFilters.action); + } + + return filtered; + } + + // Функция для рендеринга строк таблицы + function renderRequestsTable() { + const tbody = document.getElementById(`${tabId}-requests-body`); + const noRequestsDiv = document.getElementById(`${tabId}-no-requests`); + const filteredRequests = filterRequests(); + + if (filteredRequests.length === 0) { + tbody.innerHTML = ''; + noRequestsDiv.style.display = 'block'; + return; + } + + noRequestsDiv.style.display = 'none'; + + tbody.innerHTML = filteredRequests.map(request => { + + // Определяем доступные действия + const actions = []; + + // Кнопка отзыва (только для инициатора и неподтвержденных запросов) + if (request.init_user_id === userData.id && request.accepted === null) { + actions.push(` + + `); + } + + // Кнопки принятия/отклонения (в зависимости от прав) + let canDecide = false; + + // Проверяем права в зависимости от типа запроса + if (request.action === 'Возврат' && accessData.refund_request_confirm) { + canDecide = true; + } else if (request.action === 'Списание' && accessData.debit_request_confirm) { + canDecide = true; + } else if (request.action !== 'Возврат' && request.action !== 'Списание' && + (accessData.refund_request_confirm || accessData.debit_request_confirm)) { + // Для других типов запросов, если есть хотя бы одно из прав + console.warning('Unknown request action', request.action); + canDecide = true; + } + + if (canDecide) { + actions.push(` +
+ + +
+ `); + } + + // Если нет доступных действий + if (actions.length === 0) { + actions.push('Нет действий'); + } + + return ` + + + ${request.action} + ${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}`) : '-'} + ${request.target_toolbox_id ? (toolboxMap[request.target_toolbox_id] || `Склад ${request.target_toolbox_id}`) : '-'} + ${request.toolkit_id ? (toolkitMap[request.toolkit_id] || `Инструмент ${request.toolkit_id}`) : '-'} + + ${request.quantity} + + + ${request.reason || 'Нет обоснования'} + + +
+ ${actions.join('')} +
+ + + `; + }).join(''); + } + + // Функция для показа модального окна подтверждения + function showConfirmationModal(title, message, onConfirm) { + // Проверяем, есть ли уже модальное окно + let modal = document.getElementById('confirmation-modal'); + + if (!modal) { + // Создаем модальное окно + modal = document.createElement('div'); + modal.className = 'modal fade'; + modal.id = 'confirmation-modal'; + modal.tabIndex = -1; + modal.setAttribute('aria-hidden', 'true'); + modal.innerHTML = ` + + `; + document.body.appendChild(modal); + } + + // Устанавливаем содержимое + document.getElementById('confirmation-message').innerHTML = message; + + // Очищаем предыдущие обработчики + const confirmBtn = document.getElementById('confirm-action-btn'); + const oldConfirmBtn = confirmBtn.cloneNode(true); + confirmBtn.parentNode.replaceChild(oldConfirmBtn, confirmBtn); + + const newConfirmBtn = document.getElementById('confirm-action-btn'); + + // Устанавливаем новый обработчик + newConfirmBtn.addEventListener('click', function () { + const modalInstance = bootstrap.Modal.getInstance(modal); + modalInstance.hide(); + onConfirm(); + }); + + // Показываем модальное окно + const modalInstance = new bootstrap.Modal(modal); + modalInstance.show(); + } + + async function sendRequestDecision(requestId, accepted, requestResult = null) { + const data = await apiRequest('/records/', { request_id: requestId, user_id: userData.id, accepted: accepted }); + if (data.status == 'ok') { + const requestIndex = requests.findIndex(r => r.id == requestId); + if (requestIndex !== -1) { + requests.splice(requestIndex, 1); + } + // Перерисовываем таблицу + renderRequestsTable(); + + // Показываем уведомление об успехе + requestResult = requestResult === null ? (accepted ? 'Принят' : 'Отклонен') : requestResult; + showInfo(`Запрос успешно ${requestResult}`, 'success'); + } else { + const errorMessage = data.message || 'Ошибка сервера'; + showInfo(errorMessage, 'error'); + throw new Error(errorMessage); + } + } + + // Функция для обработки решения по запросу + function handleRequestDecision(requestId, accepted) { + const action = accepted ? 'принять' : 'отклонить'; + + showConfirmationModal( + `Подтверждение действия`, + `Вы уверены, что хотите ${action} этот запрос?`, + async () => { + await sendRequestDecision(requestId, accepted); + } + ); + } + + // Функция для отзыва запроса + function handleRequestWithdrawal(requestId) { + showConfirmationModal( + 'Отзыв запроса', + 'Вы уверены, что хотите отозвать этот запрос?', + async () => { + await sendRequestDecision(requestId, false, 'Отозван'); + } + ); + } + + // Функция для массовых действий + function handleBulkAction(actionType) { + const filteredRequests = filterRequests(); + + // Фильтруем только те запросы, с которыми можно совершить действие + let applicableRequests = filteredRequests; + + if (actionType === 'accept' || actionType === 'reject') { + // Для принятия/отклонения: только ожидающие решения + applicableRequests = filteredRequests.filter(r => r.accepted === null); + + // Проверяем права для каждого запроса + applicableRequests = applicableRequests.filter(r => { + if (r.action === 'Возврат') { + return accessData.refund_request_confirm; + } else if (r.action === 'Списание') { + return accessData.debit_request_confirm; + } else { + return accessData.refund_request_confirm || accessData.debit_request_confirm; + } + }); + } else if (actionType === 'withdraw') { + // Для отзыва: только мои и ожидающие решения + applicableRequests = filteredRequests.filter(r => + r.init_user_id === userData.id && r.accepted === null + ); + } + + if (applicableRequests.length === 0) { + showInfo('Нет подходящих запросов для этого действия', 'warning'); + return; + } + + const actionName = actionType === 'accept' ? 'принять' : + actionType === 'reject' ? 'отклонить' : 'отозвать'; + + showConfirmationModal( + 'Массовое действие', + `Вы уверены, что хотите ${actionName} все отправленные запросы (${applicableRequests.length})?`, + async () => { + // Отправляем запросы на сервер + const promises = applicableRequests.map(request => + sendRequestDecision(request.id, actionType === 'accept') + ); + await Promise.all(promises); + } + ); + } + + + // Назначаем обработчики событий для фильтров + document.getElementById(`${tabId}-user-filter`).addEventListener('change', function () { + currentFilters.user = this.value; + renderRequestsTable(); + }); + + document.getElementById(`${tabId}-action-filter`).addEventListener('change', function () { + currentFilters.action = this.value; + renderRequestsTable(); + }); + + // Назначаем обработчики для массовых действий + const acceptAllBtn = document.getElementById(`${tabId}-accept-all-btn`); + const rejectAllBtn = document.getElementById(`${tabId}-reject-all-btn`); + const withdrawAllBtn = document.getElementById(`${tabId}-withdraw-all-btn`); + + if (acceptAllBtn) { + acceptAllBtn.addEventListener('click', () => handleBulkAction('accept')); + } + + if (rejectAllBtn) { + rejectAllBtn.addEventListener('click', () => handleBulkAction('reject')); + } + + if (withdrawAllBtn) { + withdrawAllBtn.addEventListener('click', () => handleBulkAction('withdraw')); + } + + // Назначаем делегированные обработчики для действий в таблице + document.getElementById(`${tabId}-requests-body`).addEventListener('click', function (e) { + const target = e.target; + + // Находим ближайшую кнопку или родительскую кнопку + const button = target.closest('.accept-btn, .reject-btn, .withdraw-btn'); + if (!button) return; + + const requestId = button.dataset.requestId; + + if (button.classList.contains('accept-btn')) { + handleRequestDecision(requestId, true); + } else if (button.classList.contains('reject-btn')) { + handleRequestDecision(requestId, false); + } else if (button.classList.contains('withdraw-btn')) { + handleRequestWithdrawal(requestId); + } + }); + + // Первоначальный рендеринг таблицы + renderRequestsTable(); } -function formatValue(value) { - if (value === null || value === undefined) return '—'; - if (typeof value === 'boolean') return value ? 'Да' : 'Нет'; - if (typeof value === 'object') return JSON.stringify(value); - return value.toString(); -} +function renderJurnalToolkitsTab(tabId, tabData) { + const tabContent = document.getElementById(`${tabId}-tab-content`); + const tabOptionalContent = document.getElementById(`${tabId}-tab-optional-content`); + + const { requests, users, toolboxes, toolkits, startDate, endDate } = tabData; + + if (requests.length === 0) { + tabContent.innerHTML = ` + + `; + return; + } + + // Собираем списки для фильтров + const initUsers = [...new Set(requests.map(r => r.init_user_id))]; + const userMap = {}; + users.forEach(user => { + userMap[user.id] = user.username; + }); + + const actionTypes = [...new Set(requests.map(r => r.action))]; + + // Создаем мапу для toolboxes + const toolboxMap = {}; + toolboxes.forEach(box => { + toolboxMap[box.id] = box.title; + }); + + // Создаем мапу для toolkits + const toolkitMap = {}; + toolkits.forEach(kit => { + toolkitMap[kit.id] = kit.title; + }); + + const savedFilters = loadFromStorage(tabId); + // Фильтры + let currentFilters = { + user: savedFilters?.user || 'all', + action: savedFilters?.action || 'all', + status: savedFilters?.status || 'all' + }; + + // Рендерим дополнительный контейнер с фильтрами + tabOptionalContent.innerHTML = ` +
+ +
+
+
+
+ + + + +
+
+
+ +
+
+
+
+
+ + + + +
+
+
+
+ + + + +
+
+
+
+ + +
+
+
+ + Дата начала: + + +
+
+ + Дата окончания: + + +
+
+ +
+
+
+
+ `; + + const filterResetBtn = document.getElementById(`${tabId}-filter-reset-btn`); + filterResetBtn.addEventListener('click', () => { + currentFilters = { + user: 'all', + action: 'all', + status: 'all' + }; + document.getElementById(`${tabId}-user-filter`).value = currentFilters.user; + document.getElementById(`${tabId}-action-filter`).value = currentFilters.action; + document.getElementById(`${tabId}-status-filter`).value = currentFilters.status; + saveToStorage(tabId, currentFilters); + renderRequestsTable(); + }); + + 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') { + renderJurnalToolkitsTab(tabId, newPeriodData.data); + } + } + }); + + // Рендерим основной контейнер с таблицей запросов + tabContent.innerHTML = ` +
+
+
+ + + + + + + + + + + + + + + + +
ТипОформилРешилСо складаНа складИнструментКол-воОбоснование
+
+ +
+
+ `; + + // Функция для фильтрации запросов + function filterRequests() { + let filtered = requests; + + // Фильтр по пользователю + if (currentFilters.user !== 'all') { + filtered = filtered.filter(r => r.init_user_id == currentFilters.user); + document.getElementById(`${tabId}-user-filter`).value = currentFilters.user; + } + + // Фильтр по типу действия + if (currentFilters.action !== 'all') { + filtered = filtered.filter(r => r.action === currentFilters.action); + document.getElementById(`${tabId}-action-filter`).value = currentFilters.action; + } + + // Фильтр по статусу + if (currentFilters.status !== 'all') { + document.getElementById(`${tabId}-status-filter`).value = currentFilters.status; + switch (currentFilters.status) { + case 'accepted': + filtered = filtered.filter(r => r.accepted === true); + break; + case 'rejected': + filtered = filtered.filter(r => r.accepted === false); + break; + } + } + + return filtered; + } + + // Функция для рендеринга строк таблицы + function renderRequestsTable() { + const tbody = document.getElementById(`${tabId}-requests-body`); + const noRequestsDiv = document.getElementById(`${tabId}-no-requests`); + const filteredRequests = filterRequests(); + + if (filteredRequests.length === 0) { + tbody.innerHTML = ''; + noRequestsDiv.style.display = 'block'; + return; + } + + noRequestsDiv.style.display = 'none'; + + tbody.innerHTML = filteredRequests.map(request => { + // Определяем статус запроса + let statusBadge = ''; + if (request.accepted === true) { + statusBadge = 'Принято'; + } else { + statusBadge = 'Отклонено'; + } + + return ` + + + ${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} + ${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}`) : '-'} + + ${request.quantity} + + + ${request.reason || 'Нет обоснования'} + + + `; + }).join(''); + } + + // Назначаем обработчики событий для фильтров + document.getElementById(`${tabId}-user-filter`).addEventListener('change', function () { + currentFilters.user = this.value; + saveToStorage(tabId, currentFilters); + renderRequestsTable(); + }); + + document.getElementById(`${tabId}-action-filter`).addEventListener('change', function () { + currentFilters.action = this.value; + saveToStorage(tabId, currentFilters); + renderRequestsTable(); + }); + + document.getElementById(`${tabId}-status-filter`).addEventListener('change', function () { + currentFilters.status = this.value; + saveToStorage(tabId, currentFilters); + renderRequestsTable(); + }); + + // Первоначальный рендеринг таблицы + renderRequestsTable(); +} document.addEventListener('DOMContentLoaded', async () => { await getCookieData(); diff --git a/api/static/js/user.js b/api/static/js/user.js index ebb923a..44a5832 100644 --- a/api/static/js/user.js +++ b/api/static/js/user.js @@ -120,6 +120,9 @@ class ClientManager { // Очищаем cookie пользователя this.clearUserCookie(); + // Очищаем локальное хранилище + localStorage.clear(); + // Переход на страницу выхода setTimeout(() => { window.location.href = '/user/login'; diff --git a/db/handlers/__pycache__/access.cpython-313.pyc b/db/handlers/__pycache__/access.cpython-313.pyc index b70ed853bc7324d36672b4cdb60993d96aab1c93..96388f272de2efbd535d568501df54bffdab3f75 100644 GIT binary patch delta 686 zcmZ|MJ8#oa7=YpLdBdRUEdhBzPhn%?@=@OP^k!z$)ScYX;zLjg2+0#R5|mx4o5Hh8mO^_dL1+g)1Te z+M|G5sk-H>uCd@QRNSKB^0Br$wMlNsv0drZ297jQKMjr6+@NAR@+KIHj0S2B+Vu)CcC)V)ZXe* zPeKPo4?>PPC?Y+1lY;j`FCiy;^ddv`=EePzOH|w)e*E_Tec#M7$2X2vy+1r2H!`0u z$Cs29?xXi@81IpHVfA4^!Xh*E1`Et87DHlqDx0n|Vx-^|IWhWAJyvjwadFwYz5CQo4p-GZal7Z$y4^)kvVW zT~pe%dRvyXr>X?4b!}%Y$Lv?4F1%DSe^g%vTmxJOtN_*kHv#K_4ZtSgHlRXY#=7bM z4v*)?BkLp^e`VKinM3jb%DDPn;WF*De%*X&GZ}81MWra!eEa2!2Oe17JO#XGYdY4*!-YKcLVSd zL~Q(ZgNDGvh~@h>8Ug1f!%^_qq$dtOkJx0tNO^E!k|)5EaBzU8z|-i=*MiJTm%swBt@RKgZ45zN5CAk5&-=)t_XOZ5q(Iu_*&ri>8H3P5$fYzz$C3<{GE zFey!5s9q%P%)#K!g3Ij<-zeyYne%FAwX|tY2I5Xpp&28FVOlr?`jb~WS)ZU@C zUvsDC4(Sg5C)|7;^_RKj7bsrgR=y#ru|jZ#(3Qz=bWgJ{uU=SvUBGzqF1^Nje^$nW zMvQ)@EC-F9G=Q8V(u{rJvsmQ=ljaC@}c`VqoBAP?&sxNon#!^&)9y4hDB7zgi&4==T{+7I856od)9a z&3PK(%#5oxAJg_?VtpX3GDCZ^yq*~2<;iAxr;xIWjtua=qJi@(AY@> z$T=d-=qJH)M8=5+$T_CL=qJr`Ow)-O$T^|I=qJZ=LKm#;v?!yWBHL*(M^T{68Er;C qWwtXqj#@y@ISED(2{Z;s%7IBmFsTe8li4920h2ro44XL&_{;&$M?{tY diff --git a/db/handlers/__pycache__/records.cpython-313.pyc b/db/handlers/__pycache__/records.cpython-313.pyc index ded9ab5657f2094ab0cd3c113bd0537e85a6a1f7..dd10b1ed710b062c1e1738731d5918064d71bb76 100644 GIT binary patch delta 4340 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 DE?)xt delta 3349 zcma)8YitwQ6~1@IGkzq_%T8h^j^n&>9!{E&q$GqlE+i;jWnx~T3ywVw2FHdw4zC3Q z+C|HPw1wVXsZ!B4sw#C;Ds5DkHdPfZh==ya{;<_bu%lge|11^#1xurqR;tu{?j+$+ zesnB~|f8JwI`vu)%iiXqb)rG?qpEJRSXdfb2K}1k65xodLikEJ}{kWQ*#l1iADP{X4 zrF@^X+N8KD&>_h zuTW}-C<_1z0ZIYN=qbrVXHD1aR>ta5R2`q1AZ9uuHCb2e=?SSV^NBQraYN<}^PdG= zLCv<$r(p&00{8%$0YnD6m`oklZBaE6kE+rA;k2=-1N2VNTf&iudL*qzNi}D+G>N6- zlPakJlUjf}27WVX0Gd1Iaj&=_EeJUgX=G~6%)nbN)-_o=Y=7IAx5>C@%X>Ze!}HI(oj}N$uqhxgC6T(8lt$`=1{a_R_@( zHBEjDYB(+V5y-4+G|mpRa#$IJwsb0$7*CzpA2&|Q^R7$Ga8lFF%u6_(CS)zWRqhI` z23N0~KcDo16t>dk!^gv9LL(bN(m>6w=H4M7H-J()7EVm5x;;hW6Y*p?u@hJ$JNZn% zlpX%VQgqPOCpMU;G?lQxbXJrnwa8KtGh>9{xNo!Xvjeoc)c_ zw$^!QrN!1d21Ur95LRq!a2spbzIPDn#)dR0kCiPMAet?7(wRb&H)LWi^Fv|*3P}a1 zOIkMep^z|ANyC1JC@XeayvChpetG%cS~OO*>@Q?qUI@o}*{M>{wW9UCc8!Ajb|VyK zd22`YC}gI&HO^Y4kafRZDOw}2Sj-Nz)1nFOND~}rTM0s`iz79(m}TkL^(A&|((-a_ z8){L=x*QQ(ZmmZaH2ki{Txmkm5<@j;3|fOfW)yBkC~14{Vx*pKC@i%Xk2NjZglzOc zNoBKbqiv!@VYVA>F&sk!Vl|54UNMIKj$s1VK8tnJ=8@2rMY_=EZ4WTX6y!X+kiFr{ zoy*?HUCiEK%tG#R?t)K8i}j%VW9~fDT=3CbePuG!|JAoh^ZGr`MJRk<_D|V`z$_*q zaOR=6yyf2I1yeA4vVY0WWk1c%gA=xNbdjFx-`zHghgmGP2vOOXgjQl4KA}M=_y`2W@K! zOtankd_lic7oy{Y-5(+n0B>vQ_(7GZq!M( z@wxc?(QM_$E0zZpomb2cJl-qTFJ0biXa4SLyYK4Cxw^h|*4%dna?Ze9Q_i`DbDcS7 z=Ug=B?0Y7O?vkt06C3hu!r;QgTzGrt(sQx-VAivl`%+$Y)|zv+{^7(Yr+|W4{sQuU z#K~Wj(CtRBFpd8=*mEE~53mv}IRh+z{mO{VY@-rM6Ft_k1>es6tz$cO!=Uwx!^1&8 z9-S42AHzQdKMwkfXlZ9pPaWfQF`i@vA4{adY27r=UY2AP_=R>P-2fv1L4dOWZ_~@2 zm0obno7^aR=mY}5;y_vvz0>)i{OLBqC%J#w^#rk>kCjFEdAbGDdtD7Oj0ciOpLS{T z7eJ0?4z2zGPrDiI7mQ~81;{HIj1mvSpx^Sm@d?Zt{>R0*h!&yhY}wbnaSQE(Z36}+XDcjnd}7^1K0=fGk`Z4 zOtrw6r6c`q64}pq`rH21H6#q`aeyd;7e*#RG1bqSOBYn}Yy6J#nB8GYI+} zFVqnqmi+v@%K2c6fT;-Znki|NRSL960AAM(HkPriOvfiAuP_3pX#i*patwgy@;FdD zp>WrbP~%yie)$7l;StKT4&28IuaXPQ%x@*{!n%t9?*Uu_xXgh4`GJWI_hD4o`#`-? zUspf|@CDtozNen&2iRf1-8eT~u5QzgCc=rxL3Q#Zk?6wu@@W~wCDHMK2GXz=Sf1#- zydjIaG?k7gG^4h78S}kf-k-hnCR5R=gu0G=#MWT;)mj)l5i!PJqgCIcs&7&A*J$7? a 1: for stock in stocksMovements[1:]: @@ -298,7 +298,7 @@ class StocksActions: ) if not accept: return False - totalRecordsIds.append(recorded) + totalRecordsIds.append(recorded.id) logger.info( f"Записи {', '.join(map(str, totalRecordsIds))} о {movingRecord.action} инструмента успешно приняты {user_id}" diff --git a/db/handlers/records.py b/db/handlers/records.py index 601b6d3..a4129e4 100644 --- a/db/handlers/records.py +++ b/db/handlers/records.py @@ -1,4 +1,4 @@ -from datetime import datetime, timedelta +from datetime import date, datetime, time, timedelta from sqlalchemy import select @@ -142,39 +142,69 @@ class StocksRecordsHandler: logger.error(f"Ошибка обновления записи: {str(e)}") return False - async def get(user_id: int = None, days: int = 30): + async def getLogs(startDate: date, endDate: date): from db import CRUD try: - if user_id: - userInfo = f"пользователя {user_id} " - decided = "не решенных " - daysLimit = "" - query = select(StocksRecords).where( - StocksRecords.init_user_id == user_id, - StocksRecords.decision_user_id == None, + start_dt = datetime.combine(startDate, time.min) + end_dt = datetime.combine(endDate, time.max) + + query = ( + select(StocksRecords) + .where( + StocksRecords.created_at.between(start_dt, end_dt), + StocksRecords.decision_user_id != None, + ) + .order_by(StocksRecords.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 [] + + async def get(user_id: int, manager: bool): + from db import CRUD + + try: + if manager: + query = ( + select(StocksRecords) + .where( + StocksRecords.decision_user_id == None, + ) + .order_by(StocksRecords.created_at.asc()) ) else: - userInfo = "всех пользователей " - decided = "" - daysLimit = f"за последние {days} дн." - query = select(StocksRecords).where( - StocksRecords.created_at > datetime.now() - timedelta(days=days), + query = ( + select(StocksRecords) + .where( + StocksRecords.init_user_id == user_id, + StocksRecords.decision_user_id == None, + ) + .order_by(StocksRecords.created_at.asc()) ) - logger.debug(f"Получение всех {decided}записей {userInfo}{daysLimit}") + logger.debug(f"Получение всех записей без решения") records = await CRUD.read(query, True) - logger.debug( - f"{len(records)} {decided}записей {userInfo}{daysLimit} успешно получены" - ) - if len(records) == 0: - return [] - records.sort(key=lambda x: x.created_at, reverse=True) + logger.debug(f"{len(records)} записей без решения успешно получены") recordsData = [record.toDict() for record in records] logger.debug(recordsData) return recordsData except Exception as e: logger.error(f"Ошибка получения записей: {str(e)}") - return False + return [] async def getById(record_id: int, record: bool = False): from db import CRUD diff --git a/db/schemas/__pycache__/access.cpython-313.pyc b/db/schemas/__pycache__/access.cpython-313.pyc index 6b44e0c4cab94c546ff52089c79663bbba2b8304..9d5344d9c1130512168cf9e9804046317d1cb7e1 100644 GIT binary patch delta 178 zcmaDQwpNtyGcPX}0}wbKu+6;7Hj(ccxk0g`IL zyhZApuQ5e3GAd6tVA;Z`KKUujF-EP;YgpMB8FeRbW^-cHnEZ`xp14?1YFcStN_xH@W`nwaNiJmQP%nbi`3)?oa&oJxRe+f Z{U@7qD+#JIIxvpC_GgZ zo?0-c6ho1EFc*;22<8Ton!!9kQY)CZNPF`z^mlPIC zNlnTuK@~4%w~-Jp$%nbJxp%+Ds z8~kocDJ&4*pn6ft;sT5KO diff --git a/db/schemas/access.py b/db/schemas/access.py index a281ef6..76fe808 100644 --- a/db/schemas/access.py +++ b/db/schemas/access.py @@ -14,14 +14,10 @@ class AccessLevel(Base): updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now) # permissions - receiving_edit = Column(Boolean, default=False) - refund_request_edit = Column(Boolean, default=False) refund_request_confirm = Column(Boolean, default=False) - debit_request_edit = Column(Boolean, default=False) debit_request_confirm = Column(Boolean, default=False) tools_creation = Column(Boolean, default=False) tools_registration = Column(Boolean, default=False) - tools_registration_edit = Column(Boolean, default=False) tools_edit = Column(Boolean, default=False) tools_delete = Column(Boolean, default=False) users_creation = Column(Boolean, default=False)