diff --git a/api/routers/__pycache__/toolkit.cpython-313.pyc b/api/routers/__pycache__/toolkit.cpython-313.pyc
index db09add..dbc618c 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 c37001d..d8e4253 100644
--- a/api/routers/toolkit.py
+++ b/api/routers/toolkit.py
@@ -18,6 +18,8 @@ def handleResult(result: dict, response: dict) -> 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) {
` : '
@@ -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 `
+
+
+ ${compatibleToolkit?.title || 'Неизвестный инструмент'}
+ ${compatibleToolkit ? `
+
+
+
+ ` : ''}
+
+ ${compatibleCategory?.title || 'Неизвестно'}
+ ${compatibleToolkit?.comment_text || 'Нет комментария'}
+ ${accessData.tools_edit ? `
+
+
+
+
+
+ ` : ''}
+
+ `;
+ }).join('')}
+
+
+
+ `;
+ } 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 = `
+
+
+
+
+
+
+
+
+
+
+
+
+ Название
+ Категория
+ Описание
+
+
+
+ ${filteredToolkits.map(toolkit => {
+ const category = categories[toolkit.category_id];
+ return `
+
+
+
+
+
+
+ ${toolkit.title}
+ ${category?.title || 'Неизвестно'}
+ ${toolkit.description || 'Нет описания'}
+
+ `;
+ }).join('')}
+
+
+
+ ${filteredToolkits.length === 0 ? `
+
+
+ Нет доступных инструментов для добавления
+
+ ` : ''}
+
+
+
+
+ `;
+
+ 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 08700df..9bd7096 100644
Binary files a/db/handlers/__pycache__/orders.cpython-313.pyc and b/db/handlers/__pycache__/orders.cpython-313.pyc differ
diff --git a/db/handlers/__pycache__/toolkit.cpython-313.pyc b/db/handlers/__pycache__/toolkit.cpython-313.pyc
index e54d0d7..0f0bdcf 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/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 da0a154..500f967 100644
Binary files a/db/schemas/__pycache__/toolkit.cpython-313.pyc and b/db/schemas/__pycache__/toolkit.cpython-313.pyc differ
diff --git a/db/schemas/toolkit.py b/db/schemas/toolkit.py
index b6ab954..5c0f62f 100644
--- a/db/schemas/toolkit.py
+++ b/db/schemas/toolkit.py
@@ -1,9 +1,154 @@
from datetime import datetime
-from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, Text
+from sqlalchemy import (
+ Boolean,
+ Column,
+ DateTime,
+ ForeignKey,
+ Integer,
+ String,
+ Text,
+ select,
+)
from sqlalchemy.dialects.postgresql import JSONB
-from sqlalchemy.orm import relationship
from db import Base, CRUD
import utils
+from sqlalchemy import Column, Integer, ForeignKey, UniqueConstraint, CheckConstraint
+
+
+class ToolkitCompatibility(Base):
+ __tablename__ = "toolkit_compatibility"
+
+ id = Column(Integer, primary_key=True)
+ toolkit_id = Column(
+ Integer, ForeignKey("toolkits.id", ondelete="CASCADE"), nullable=False
+ )
+ compatible_toolkit_id = Column(
+ Integer, ForeignKey("toolkits.id", ondelete="CASCADE"), nullable=False
+ )
+ __table_args__ = (
+ # Запрещаем дубли (A-B и A-B)
+ UniqueConstraint(
+ "toolkit_id", "compatible_toolkit_id", name="uq_toolkit_compatibility"
+ ),
+ # Запрещаем связь с самим собой
+ CheckConstraint(
+ "toolkit_id <> 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):