From 599158f8e1f30f597821c328e6effa1a0aa765b2 Mon Sep 17 00:00:00 2001 From: Macbook Date: Sun, 21 Dec 2025 19:02:40 +0300 Subject: [PATCH] release --- .../__pycache__/toolkit.cpython-313.pyc | Bin 15125 -> 17878 bytes api/routers/toolkit.py | 39 ++ api/static/css/index.css | 4 + api/static/js/index.js | 412 +++++++++++++++++- .../__pycache__/orders.cpython-313.pyc | Bin 7506 -> 7964 bytes .../__pycache__/toolkit.cpython-313.pyc | Bin 19249 -> 21536 bytes db/handlers/orders.py | 9 + db/handlers/toolkit.py | 53 ++- .../__pycache__/toolkit.cpython-313.pyc | Bin 2732 -> 8844 bytes db/schemas/toolkit.py | 149 ++++++- 10 files changed, 639 insertions(+), 27 deletions(-) diff --git a/api/routers/__pycache__/toolkit.cpython-313.pyc b/api/routers/__pycache__/toolkit.cpython-313.pyc index db09add1286cbd5fcd6361e719bfd0dcca0b0aee..dbc618c8f1fcb3eea43cdfd412a7dc19f3427f21 100644 GIT binary patch delta 3291 zcmaJ@eQZsEprvjIQ)sDU8!!Yr-1oxx zkQOJcO2n!acEwt%ssc7?AfYM6LPA>CA-X^I$0|L?XaOtLI%(22Z9<8lfAGh&opWu2 zld8MYJNMjk&$%Dx{LZ=W%IQDZ zu5J#rTz=xLSnCjui^oYPDnp}nFBJTNR!}Hiw@RU&y+dqjf1nMmUtKlXaJ1dm!5r#; z6~70IT@eCgSYWAQE2|OxVnCA})aN(r0-c~v5Qc?-Mg#mX31}L4Un}^uY(%WpX2ft% z4zly&p<*Y@Sm2Wa?5|>tzgRF@6R#~532yJK?vg#Fb38JXUOMp6=;cxNR@&fwR9$!N z$km_B_Fp=XGP;tLnm8eD_P)0M5VgZ9J`F8_E+a-`N8%av(TGfqI95kv@-2~ifTyKM zYEfDw$K&HCqOv5y$ODZ!P;3CR!?`Ki1QJJ5U(}?rdx5>BqBiLGW*KoT9zIKJ*`V=8 zLpxgPPE7`5a#TJ`J8-C<7!QUbCnGW0r=qApH!!QoL+q^6w9D&3!OO#FB{?2CAyGey zTak3IPfcUQ!TxKi(Ch?3LvoGz15Lv&oa2YU1JSie_;n1S_(|OC&Bz&<`q`h#n-8OR zGx}X;LXinM8jn%5@D+W}4>`l^-WZJxiAY7zqf?;e{1Oa@59d{fV0I*Vz`9>;*b8j# zKWUy-oW%$mo7{%3kTk z5YRV}1c1o!T8G*gXWGsFVXs?1g6u&gKSHtrh=fQi zxQp>fbbOo#c`Sa09)ihea>tt6V#Ql%vC6xCCc1Z$Ha6g9Wn(b!(-(Nq&z`u$E)E<^ zC@4pc#%VMn9UcqHp=0zoXrD{Qs?F+(6Dt>9hr>2@*<0S7WpR8GNhK0~J+a_=kWg?v zAU-h*8h*b6>|1Y@gKus|)V@3bjf3u$q^;&_m1QMP02v$xfpb^CDx&rR7d2A6a-6o{%NhS~b(4cQ<);ZA#lWgLbQ8 ziECx_xC{h-Aj~r-duw!gMnqh|$xp^7V=~={3!G=4ckCxlrtWN+!Wx%7A`9isynoi9q_kjB{%Xwh%J7iOd)VrtvqZw2V5jOFP)!GZ^ zsu4W=!3wC>ky!rOGU=KPYS^8(fQD@^vlfqHx#Nwc%G|DDRi`RD?96#K-^WyJyvbt? zXopp1pS9oU`l0F?3o6zwZOW(}xk5py26$z6DH z#}8ke{int@hnh}r3qp^$@&?`r1MlH!SQxuiL(W6m$Ra`R`2;h!FM`u&7 z5tfkNXkSxu35YSB`DsrRG4%YC2j{#E<0yRLW5>C>*w zX=_*7(w!Js1nhE9j3@_#%E*{v2|X0Ui&pP~wSC^&J~xoIZe1{MP4q36I6v-7m-rL? zj|}CB*OoOxNzZes47Qr-)%RAD2L{qxB|b2g0sq||5G%h_exFJxqjLa}-y#A46L&p7 z;rUh5mysPWp z?zC%Qp>klhZ_!qBtuJj`Kij`#=0AqdOoF+RD{h^4ZM}Co?HXFB99p5M%l|=|OBRh~ z9}Zm{Qk=~K>ABYsuF`5pq2eZEGP-tg}!{#tR*;tyg*oQOlp?F5z%A;BLC{`m7}nBqf8r;%V; z=S7oZCUFPR{m8kH@P}O2eKH=N9FJ_Jw?T&0N4f~LXh}^7`HN8hjZpQCz*;xG;wU24 zkF84rbT_lzTN_qg^Z-wJIez=rRNCeCT6 mVyaRaq=d6UA@bwK);UAEv3pstfQ|JTc4#w6mQ}#=TmKKHdPh0{ delta 1562 zcmZWpU2NM_6t)xFaekV1BW;tE(zHvPdQID{tev#M5dBdpu#IXfl?EbpZ7;K$#Obwz znip1h0HpDMH3_sQ_JBa*0r1NnhcpRZ_OJ;dVX-$}CZ?(rNIXsAoNI4OGfVk=?mg$8 zob%o9`1ecv<+qdnbaccxcm}ye^J?MqWID7H`f+q8B$UULBE1*>JMt=cFLP(;V6>Nh zl9uTMVMJQt^PD15Rm{^%;+wH7gml5rK1%P42lh)`BDH;bH_SaV^r8G&`j(t^XC;g0 z`{_@S5y=yD)luo4$kWFafegSgvJc?^fERHrtFmD_wg9UfJS2nL@v`BR0vAzWC+!}Q z4~qSRU@9Tf3onw#Kw;g|n=Dmhi7rdM^ox%73y0AoZ#LALV>(STh0AEYqH0Ffs5x*R z1IRReA(`XzbS=3s)K-;bJC?R#lOo2ABTUg>lIwk}w$7lg5-~O#hV7792pD$fJ3kT& za~R92!%E2*0_!ubSylY=v&NP~iuAwKSP4n=q6=G^QFlzMMp$o=CkP4-`n4eG9&gwY zcq3cz1ca%&UV0e%GI&?qce|Iv@mIhM3Zi$?Q{9uW{*F_4G75&x13&_r&y1%waU%+_ z&0Sl)zIyxllcbLtnZqYe!5%pcpzxlosg6;$Nb})Q^u30#i~zqN#r4r&kKUd=11WyX zjP~MHDKhEAm|=tj!ZG?(c6eq5&DRlD5vBob5ul~H#Cfw)VRBfvw#Yefmfc$Rd!hRc zJkdsXkxmZG^OMvWp!?UruS8oYEz(R*=QH%3T#tAGoLlZ!xnx*3_vV?vB{Jp3b;H(( z$u39(hSg`!9;U7(_TJy>Y5NtD21lm&uFTrhRx(Foz?e&b##Toe28lG-@)BvoS) zEYkY;@t(IJ!V@&B-t-$T;fXc+^Y~R&MHA~G>ueKXPh}47;?#KYU`pm;_I#*1*wb#F}Ek`}iyP20Un^v={UyXcaQ~PY3WZN8!}RWp_f$sU2*j{Yn$;N1}< zhhS dict: response["message"] = result["errorMessage"] else: response["status"] = "ok" + if "data" in result.keys(): + response["data"] = result["data"] return response @@ -251,3 +253,40 @@ async def quick_action(reqData: dict = Depends(requestDict)): case _: pass return response + + +@router.get("/all", summary="Получение инструментов") +async def get_toolkits(reqData: dict = Depends(requestDict)): + logger.info(f"Получение инструментов") + return {"status": "ok", "data": await ToolkitHandler.getAll()} + + +@router.get("/compatibility", summary="Получение совместимости инструментов") +async def get_compatibility(reqData: dict = Depends(requestDict)): + response = {"status": "error"} + toolkitId = reqData.get("query").get("toolkitId") + toolkitId = int("".join(filter(str.isdigit, toolkitId))) + logger.info(f"Получение совместимости инструмента {toolkitId}") + toolkit = await ToolkitHandler.getCompatibility(toolkitId) + response = handleResult(toolkit, response) + return response + + +@router.post("/compatibility", summary="Управление совместимостью инструментов") +async def compatibility(reqData: dict = Depends(requestDict)): + logger.info(f"Управление совместимостью инструментов") + response = {"status": "error"} + action = reqData.get("body").get("action") + userId = reqData.get("body").get("userId") + data = reqData.get("body").get("data") + match action: + case "add": + toolkit = await ToolkitHandler.addCompatibility(userId, data) + response = handleResult(toolkit, response) + case "delete": + toolkit = await ToolkitHandler.deleteCompatibility(userId, data) + response = handleResult(toolkit, response) + case _: + logger.error(f"Unknown action: {action}") + pass + return response diff --git a/api/static/css/index.css b/api/static/css/index.css index 44c5d53..21048ba 100644 --- a/api/static/css/index.css +++ b/api/static/css/index.css @@ -459,4 +459,8 @@ tr:hover .action-buttons { .modal-footer { padding: 0.75rem 1rem; } +} + +.modal-content-green { + background-color: #f4fbf6; /* светло-зелёный */ } \ No newline at end of file diff --git a/api/static/js/index.js b/api/static/js/index.js index 10ea46a..d8ae7d0 100644 --- a/api/static/js/index.js +++ b/api/static/js/index.js @@ -1,6 +1,6 @@ import { getCookie } from '/static/js/cookies.js'; import { apiRequest } from '/static/js/api.js'; -import { showInfo } from '/static/js//toast.js'; +import { showInfo } from '/static/js/toast.js'; let accessData; let userData; @@ -3836,14 +3836,16 @@ async function getToolkitStocks(toolkitId) { return resp.data; } -// Функция показа модального окна с деталями инструмента // Функция показа модального окна с деталями инструмента async function showToolkitDetailsModal(toolkitId) { const modalId = 'toolkitDetailsModal'; let modal = document.getElementById(modalId); if (modal) { - modal.hide(); + const modalInstance = bootstrap.Modal.getInstance(modal); + if (modalInstance) { + modalInstance.hide(); + } modal.remove(); } @@ -3930,9 +3932,12 @@ async function showToolkitDetailsModal(toolkitId) { ` : '
'; } - // Переменная для хранения данных об остатках (будет загружена при раскрытии аккордеона) + // Переменные для хранения данных let toolkitStocksData = null; let isStocksLoading = false; + let compatibilityData = null; + let isCompatibilityLoading = false; + let allToolkitsData = null; // Форматирование даты комментария let commentDateInfo = ''; @@ -4028,6 +4033,8 @@ async function showToolkitDetailsModal(toolkitId) { + + ${toolkitData.external_link ? ` + + +
+
+

+ +

+
+
+
+
+ Загрузка... +
+

Загрузка данных о совместимости...

+
+
+ +
+
+ + Не удалось загрузить данные о совместимости +
+ ${accessData.tools_edit ? ` + + ` : ''} +
+
+
+
@@ -4140,8 +4186,6 @@ async function showToolkitDetailsModal(toolkitId) { // Показываем результат if (response.status === 'ok') { - // Успешное отображение - // Обновляем список инструментов if (typeof uploadTab === 'function') { await uploadTab('toolkits'); @@ -4370,26 +4414,362 @@ async function showToolkitDetailsModal(toolkitId) { } }; - // Обработчик события раскрытия аккордеона + // Функция для загрузки данных о совместимости + const loadCompatibilityData = async () => { + if (isCompatibilityLoading) return; + + const compatibilityLoading = modal.querySelector('#compatibilityLoading'); + const compatibilityContent = modal.querySelector('#compatibilityContent'); + const compatibilityError = modal.querySelector('#compatibilityError'); + const addCompatibilityBtn = modal.querySelector('#addCompatibilityBtn'); + + try { + isCompatibilityLoading = true; + + // Показываем спиннер, скрываем контент и ошибку + compatibilityLoading.classList.remove('d-none'); + compatibilityContent.classList.add('d-none'); + compatibilityError.classList.add('d-none'); + + // Загружаем данные о совместимости + const response = await apiRequest(`/toolkit/compatibility?toolkitId=${toolkitData.id}`, {}, 'GET'); + + if (response.status === 'ok') { + compatibilityData = response.data; + + // Формируем HTML для совместимости + let compatibilityHtml = ''; + + if (Object.keys(compatibilityData.records || {}).length > 0) { + compatibilityHtml = ` +
+ + + + + + + ${accessData.tools_edit ? '' : ''} + + + + ${Object.entries(compatibilityData.records || {}).map(([recordId, compatibleToolkitId]) => { + const compatibleToolkit = compatibilityData.toolkits[compatibleToolkitId]; + const compatibleCategory = categories[compatibleToolkit?.category_id]; + + return ` + + + + + ${accessData.tools_edit ? ` + + ` : ''} + + `; + }).join('')} + +
ИнструментКатегорияКомментарийДействия
+ ${compatibleToolkit?.title || 'Неизвестный инструмент'} + ${compatibleToolkit ? ` + + ` : ''} + ${compatibleCategory?.title || 'Неизвестно'}${compatibleToolkit?.comment_text || 'Нет комментария'} + +
+
+ `; + } else { + compatibilityHtml = ` +
+ + Нет данных о совместимых инструментах +
+ `; + } + + // Вставляем HTML и показываем контент + compatibilityContent.innerHTML = compatibilityHtml; + compatibilityContent.classList.remove('d-none'); + + // Показываем кнопку добавления, если есть права + if (accessData.tools_edit) { + addCompatibilityBtn.style.display = 'inline-block'; + } + + // Добавляем обработчики для кнопок просмотра инструмента + compatibilityContent.querySelectorAll('.view-toolkit-btn').forEach(button => { + button.addEventListener('click', async (e) => { + e.preventDefault(); + e.stopPropagation(); + + const compatibleToolkitId = e.currentTarget.dataset.toolkitId; + + const modalInstance = bootstrap.Modal.getInstance(modal); + if (modalInstance) { + modalInstance.hide(); + } + + modal.addEventListener('hidden.bs.modal', async () => { + await showToolkitDetailsModal(compatibleToolkitId); + }, { once: true }); + }); + }); + + // Добавляем обработчики для кнопок удаления + compatibilityContent.querySelectorAll('.delete-compatibility-btn').forEach(button => { + button.addEventListener('click', async (e) => { + e.preventDefault(); + e.stopPropagation(); + const recordId = e.currentTarget.dataset.recordId; + const compatibleToolkitId = e.currentTarget.dataset.toolkitId; + + if (confirm('Вы уверены, что хотите удалить эту связь совместимости?')) { + await deleteCompatibility(recordId, compatibleToolkitId); + } + }); + }); + } else { + throw new Error(response.message || 'Ошибка загрузки данных о совместимости'); + } + } catch (error) { + console.error('Ошибка при загрузке данных о совместимости:', error); + compatibilityError.classList.remove('d-none'); + } finally { + compatibilityLoading.classList.add('d-none'); + isCompatibilityLoading = false; + } + }; + + // Функция для удаления совместимости + const deleteCompatibility = async (recordId, compatibleToolkitId) => { + try { + const response = await apiRequest('/toolkit/compatibility', { + action: 'delete', + userId: userData.id, + data: { + toolkitId: toolkitData.id, + compatibleToolkitId: compatibleToolkitId + } + }, 'POST'); + + if (response.status === 'ok') { + showInfo('Связь совместимости успешно удалена', 'success'); + // Обновляем данные + compatibilityData = null; + await loadCompatibilityData(); + } else { + throw new Error(response.message || 'Ошибка удаления связи'); + } + } catch (error) { + console.error('Ошибка при удалении совместимости:', error); + showInfo(error.message || 'Произошла ошибка при удалении связи', 'danger'); + } + }; + + // Функция для добавления совместимости + const addCompatibility = async (compatibleToolkitId) => { + try { + const response = await apiRequest('/toolkit/compatibility', { + action: 'add', + userId: userData.id, + data: { + toolkitId: toolkitData.id, + compatibleToolkitId: compatibleToolkitId + } + }, 'POST'); + + if (response.status === 'ok') { + showInfo('Связь совместимости успешно добавлена', 'success'); + // Обновляем данные + compatibilityData = null; + await loadCompatibilityData(); + // Закрываем модальное окно добавления + const addModal = bootstrap.Modal.getInstance(document.getElementById(`${toolkitData.id}-add-compatibility-modal`)); + if (addModal) addModal.hide(); + } else { + throw new Error(response.message || 'Ошибка добавления связи'); + } + } catch (error) { + console.error('Ошибка при добавлении совместимости:', error); + showInfo(error.message || 'Произошла ошибка при добавлении связи', 'danger'); + } + }; + + // Функция для показа модального окна добавления совместимости + const showAddCompatibilityModal = async () => { + // Загружаем все инструменты, если еще не загружены + if (!allToolkitsData) { + try { + const response = await apiRequest('/toolkit/all', {}, 'GET'); + if (response.status === 'ok') { + allToolkitsData = response.data; + } + } catch (error) { + console.error('Ошибка загрузки списка инструментов:', error); + showInfo('Не удалось загрузить список инструментов', 'danger'); + return; + } + } + + // Создаем модальное окно + const addModalId = `${toolkitData.id}-add-compatibility-modal`; + let addModal = document.getElementById(addModalId); + + if (addModal) { + addModal.remove(); + } + + addModal = document.createElement('div'); + addModal.className = 'modal fade'; + addModal.id = addModalId; + addModal.tabIndex = -1; + + // Фильтруем инструменты: исключаем текущий и уже совместимые + const compatibleIds = Object.values(compatibilityData?.records || {}); + const filteredToolkits = Object.values(allToolkitsData || {}).filter(toolkit => + toolkit.id !== toolkitData.id && !compatibleIds.includes(toolkit.id) + ); + + addModal.innerHTML = ` + + `; + + document.body.appendChild(addModal); + const bsAddModal = new bootstrap.Modal(addModal); + bsAddModal.show(); + + // Функция поиска инструментов + const searchInput = addModal.querySelector('#compatibilitySearch'); + const toolkitsList = addModal.querySelector('#compatibilityToolkitsList'); + const rows = toolkitsList.querySelectorAll('tr'); + + searchInput.addEventListener('input', function () { + const searchTerm = this.value.toLowerCase(); + + rows.forEach(row => { + const title = row.dataset.toolkitTitle; + const isVisible = title.includes(searchTerm); + row.style.display = isVisible ? '' : 'none'; + }); + }); + + // Обработчики для кнопок выбора + addModal.querySelectorAll('.select-compatibility-btn').forEach(button => { + button.addEventListener('click', async (e) => { + e.preventDefault(); + e.stopPropagation(); + const compatibleToolkitId = e.currentTarget.dataset.toolkitId; + await addCompatibility(compatibleToolkitId); + }); + }); + + // Очистка при закрытии + addModal.addEventListener('hidden.bs.modal', () => { + addModal.remove(); + }); + }; + + // Обработчик события раскрытия аккордеона остатков const stocksCollapse = modal.querySelector('#stocksCollapse'); stocksCollapse.addEventListener('show.bs.collapse', async () => { - // Загружаем данные только если они еще не загружены if (!toolkitStocksData && !isStocksLoading) { await loadToolkitStocks(); } }); - // Обработчик для принудительной перезагрузки данных (например, при повторном открытии аккордеона) - const stocksHeading = modal.querySelector('#stocksHeading'); - stocksHeading.addEventListener('click', async (e) => { - // Если данные уже загружены, можно обновить их при повторном клике - const isExpanded = stocksCollapse.classList.contains('show'); - if (isExpanded && toolkitStocksData) { - // Можно добавить кнопку обновления или обновлять автоматически - // Для простоты пока оставляем как есть + // Обработчик события раскрытия аккордеона совместимости + const compatibilityCollapse = modal.querySelector('#compatibilityCollapse'); + compatibilityCollapse.addEventListener('show.bs.collapse', async () => { + if (!compatibilityData && !isCompatibilityLoading) { + await loadCompatibilityData(); } }); + // Обработчик для кнопки добавления совместимости + const addCompatibilityBtn = modal.querySelector('#addCompatibilityBtn'); + if (addCompatibilityBtn) { + addCompatibilityBtn.addEventListener('click', (e) => { + e.preventDefault(); + e.stopPropagation(); + showAddCompatibilityModal(); + }); + + // Остановка всплытия события при клике на кнопку внутри аккордеона + const compatibilityHeading = modal.querySelector('#compatibilityHeading'); + compatibilityHeading.addEventListener('click', (e) => { + if (addCompatibilityBtn.contains(e.target)) { + e.stopPropagation(); + } + }); + } + // Очистка при закрытии модалки modal.addEventListener('hidden.bs.modal', () => { modal.remove(); diff --git a/db/handlers/__pycache__/orders.cpython-313.pyc b/db/handlers/__pycache__/orders.cpython-313.pyc index 08700dfd6034fed8d2380fb02e460cdacdcd1440..9bd7096c49745725b7d1a0367ebf37029c6b0b6d 100644 GIT binary patch delta 2009 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 diff --git a/db/handlers/orders.py b/db/handlers/orders.py index d0e9e1b..4f391c1 100644 --- a/db/handlers/orders.py +++ b/db/handlers/orders.py @@ -1,6 +1,7 @@ from datetime import date, datetime, time from sqlalchemy import func, select from db import CRUD +from db.handlers.records import ServiceRecordsHandler from db.schemas.orders import Orders from utils.loggers import logger @@ -10,6 +11,10 @@ class OrdersHandler: async def new(user_id: int, order: str): try: await Orders(customer_id=user_id, customer_comment=order).save() + await ServiceRecordsHandler.add( + user_id, + {f"Добавлен заказ": f"{order}"}, + ) except Exception as e: logger.error(f"Ошибка создания заказа: {str(e)}") return {"errorMessage": f"Ошибка создания заказа: {str(e)}"} @@ -92,6 +97,10 @@ class OrdersHandler: if comment: changeData["executor_comment"] = comment await order.edit(**changeData) + await ServiceRecordsHandler.add( + user_id, + {f"Обновлен заказ": f"{order.customer_comment}"}, + ) except Exception as e: logger.error(f"Ошибка обновления заказа: {str(e)}") return {"errorMessage": f"Ошибка обновления заказа: {str(e)}"} diff --git a/db/handlers/toolkit.py b/db/handlers/toolkit.py index 7d395c9..e30f2b0 100644 --- a/db/handlers/toolkit.py +++ b/db/handlers/toolkit.py @@ -3,7 +3,7 @@ from db.handlers.stock import StockHandler from db.handlers.user import UserHandler from utils import logger, saveImage, safeFilename from db import CRUD -from db.schemas.toolkit import Toolkit +from db.schemas.toolkit import Toolkit, ToolkitCompatibility from sqlalchemy import select from db.handlers.records import ServiceRecordsHandler from utils.image import deleteImage @@ -173,7 +173,7 @@ class ToolkitHandler: @staticmethod async def getAll(): - query = select(Toolkit) + query = select(Toolkit).order_by(Toolkit.id) toolkits = await CRUD.read(query, True) return [toolkit.toDict() for toolkit in toolkits] if toolkits else [] @@ -184,16 +184,15 @@ class ToolkitHandler: if not toolkit: logger.error("Инструмент не найден") return {} + data = toolkit.toDict() if toolkit.comment_user_id: user_data = await UserHandler.get(toolkit.comment_user_id) - data = toolkit.toDict() - data["comment_user_data"] = user_data - logger.info(data) + data["comment_user_data"] = user_data return data @staticmethod async def getSeveral(toolkitIds: list[int]) -> list[dict]: - query = select(Toolkit).where(Toolkit.id.in_(toolkitIds)) + query = select(Toolkit).where(Toolkit.id.in_(toolkitIds)).order_by(Toolkit.id) toolkits = await CRUD.read(query, True) return [toolkit.toDict() for toolkit in toolkits] if toolkits else [] @@ -230,9 +229,6 @@ class ToolkitHandler: @staticmethod async def addComment(toolkitId: int, user_id: int, comment: str): - logger.info(f"Добавление комментария к инструменту {toolkitId}...") - logger.info(f"Комментарий: {comment}") - logger.info(f"Пользователь: {user_id}") query = select(Toolkit).where(Toolkit.id == toolkitId) toolkit = await CRUD.read(query) if not toolkit: @@ -248,6 +244,45 @@ class ToolkitHandler: logger.info(f"Комментарий к инструменту {toolkit.title} успешно добавлен") return {"status": "ok"} + @staticmethod + async def addCompatibility(userId, data): + newCompatibility = await ToolkitCompatibility.add_compatibility( + int(data.get("toolkitId")), int(data.get("compatibleToolkitId")) + ) + if "errorMessage" not in newCompatibility: + await ServiceRecordsHandler.add( + userId, + { + f"Добавлена совместимость": f"{data.get('toolkitId')} - {data.get('compatibleToolkitId')}" + }, + ) + return newCompatibility + + @staticmethod + async def deleteCompatibility(userId, data): + deleteCompatibility = await ToolkitCompatibility.remove_compatibility( + int(data.get("toolkitId")), int(data.get("compatibleToolkitId")) + ) + if "errorMessage" not in deleteCompatibility: + await ServiceRecordsHandler.add( + userId, + { + f"Удалена совместимость": f"{data.get('toolkitId')} - {data.get('compatibleToolkitId')}" + }, + ) + return deleteCompatibility + + @staticmethod + async def getCompatibility(toolkitId: int): + result = await ToolkitCompatibility.get_compatibility(toolkitId) + if "errorMessage" in result: + return result + toolkitsIds = list(result.get("data").values()) + toolkitsList = await ToolkitHandler.getSeveral(toolkitsIds) + toolkitsData = {toolkit["id"]: toolkit for toolkit in toolkitsList} + data = {"records": result.get("data"), "toolkits": toolkitsData} + return {"status": "ok", "data": data} + @staticmethod async def initialize(): from .categories import CategoryHandler diff --git a/db/schemas/__pycache__/toolkit.cpython-313.pyc b/db/schemas/__pycache__/toolkit.cpython-313.pyc index da0a15452054d94c720d0a8c12a9c053576c7e01..500f967fab9ead1c0213b5ee12772c8ff6d10cf0 100644 GIT binary patch literal 8844 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 compatible_toolkit_id", name="ck_toolkit_not_self" + ), + ) + + def __init__(self, **kwargs): + for key, value in kwargs.items(): + setattr(self, key, value) + + def toDict(self): + return { + "id": self.id, + "toolkit_id": self.toolkit_id, + "compatible_toolkit_id": self.compatible_toolkit_id, + } + + @staticmethod + def getUnique( + compatibles: list["ToolkitCompatibility"], originalToolkitId: int + ) -> dict: + unique = [] + uniqueData = {} + for c in compatibles: + if c.toolkit_id not in unique and c.toolkit_id != originalToolkitId: + unique.append(c.toolkit_id) + uniqueData[c.id] = c.toolkit_id + if ( + c.compatible_toolkit_id not in unique + and c.compatible_toolkit_id != originalToolkitId + ): + unique.append(c.compatible_toolkit_id) + uniqueData[c.id] = c.compatible_toolkit_id + return uniqueData + + async def save(self): + await CRUD.create(self) + + @staticmethod + async def add_compatibility(a_id: int, b_id: int): + errorMsg = { + "status": "error", + } + + if a_id == b_id: + utils.logger.error("Невозможно добавить совместимость с самим собой") + errorMsg["errorMessage"] = "Невозможно добавить совместимость с самим собой" + return errorMsg + + toolkit_id, compatible_id = sorted([a_id, b_id]) + try: + + await ToolkitCompatibility( + toolkit_id=toolkit_id, compatible_toolkit_id=compatible_id + ).save() + + return {"status": "ok"} + + except Exception as e: + utils.logger.error(f"Ошибка добавления совместимости: {str(e)}") + errorMsg["errorMessage"] = f"Ошибка добавления совместимости: {str(e)}" + return errorMsg + + @staticmethod + async def remove_compatibility(a_id: int, b_id: int): + errorMsg = { + "status": "error", + } + if a_id == b_id: + utils.logger.error("Невозможно удалить совместимость с самим собой") + errorMsg["errorMessage"] = "Невозможно удалить совместимость с самим собой" + return errorMsg + + toolkit_id, compatible_id = sorted([a_id, b_id]) + + try: + compatibility = await CRUD.read( + select(ToolkitCompatibility).where( + ToolkitCompatibility.toolkit_id == toolkit_id, + ToolkitCompatibility.compatible_toolkit_id == compatible_id, + ) + ) + + if not compatibility: + utils.logger.error("Совместимость не найдена") + errorMsg["errorMessage"] = "Совместимость не найдена" + return errorMsg + + await CRUD.delete(compatibility) + + return {"status": "ok"} + + except Exception as e: + utils.logger.error(f"Ошибка удаления совместимости: {str(e)}") + errorMsg["errorMessage"] = f"Ошибка удаления совместимости: {str(e)}" + return errorMsg + + @staticmethod + async def get_compatibility(toolkit_id: int): + try: + + result = await CRUD.read( + select(ToolkitCompatibility).where( + (ToolkitCompatibility.toolkit_id == toolkit_id) + | (ToolkitCompatibility.compatible_toolkit_id == toolkit_id) + ), + True, + ) + if not result: + return {"status": "ok", "data": {}} + + return { + "status": "ok", + "data": ToolkitCompatibility.getUnique(result, toolkit_id), + } + + except Exception as e: + utils.logger.error(f"Ошибка получения совместимости: {str(e)}") + return {"errorMessage": f"Ошибка получения совместимости: {str(e)}"} class Toolkit(Base):