This commit is contained in:
2025-12-21 19:02:40 +03:00
parent 546c70cbcd
commit 599158f8e1
10 changed files with 639 additions and 27 deletions
Binary file not shown.
+39
View File
@@ -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
+4
View File
@@ -460,3 +460,7 @@ tr:hover .action-buttons {
padding: 0.75rem 1rem;
}
}
.modal-content-green {
background-color: #f4fbf6; /* светло-зелёный */
}
+396 -16
View File
@@ -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) {
` : '<div class="col-md-4"></div>';
}
// Переменная для хранения данных об остатках (будет загружена при раскрытии аккордеона)
// Переменные для хранения данных
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) {
</div>
</div>
${toolkitData.external_link ? `
<div class="mt-3">
<a href="${toolkitData.external_link}" target="_blank" class="btn btn-sm btn-outline-primary">
@@ -4038,6 +4045,45 @@ async function showToolkitDetailsModal(toolkitId) {
</div>
</div>
<!-- Аккордеон для совместимости инструментов -->
<div class="accordion mt-3" id="compatibilityAccordion">
<div class="accordion-item">
<h2 class="accordion-header" id="compatibilityHeading">
<button class="accordion-button collapsed" type="button"
data-bs-toggle="collapse" data-bs-target="#compatibilityCollapse"
aria-expanded="false" aria-controls="compatibilityCollapse">
<i class="bi bi-link-45deg me-2"></i>Совместимые инструменты
</button>
</h2>
<div id="compatibilityCollapse" class="accordion-collapse collapse"
aria-labelledby="compatibilityHeading" data-bs-parent="#compatibilityAccordion">
<div class="accordion-body">
<div id="compatibilityLoading" class="text-center py-3">
<div class="spinner-border spinner-border-sm text-primary" role="status">
<span class="visually-hidden">Загрузка...</span>
</div>
<p class="mt-2 text-muted">Загрузка данных о совместимости...</p>
</div>
<div id="compatibilityContent" class="d-none">
<!-- Содержимое будет загружено динамически -->
</div>
<div id="compatibilityError" class="d-none text-center text-danger py-3 mb-3">
<i class="bi bi-exclamation-triangle me-2"></i>
<span>Не удалось загрузить данные о совместимости</span>
</div>
${accessData.tools_edit ? `
<button class="btn btn-sm btn-outline-success ms-auto me-2"
id="addCompatibilityBtn"
style="display: none;">
<i class="bi bi-plus-circle me-1"></i>Добавить совместимый инструмент
</button>
` : ''}
</div>
</div>
</div>
</div>
<!-- Секция комментариев -->
<div class="border-top pt-3 mt-3">
<h6><i class="bi bi-chat-left-text me-1"></i>Комментарий</h6>
@@ -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 = `
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th>Инструмент</th>
<th>Категория</th>
<th>Комментарий</th>
${accessData.tools_edit ? '<th width="50">Действия</th>' : ''}
</tr>
</thead>
<tbody>
${Object.entries(compatibilityData.records || {}).map(([recordId, compatibleToolkitId]) => {
const compatibleToolkit = compatibilityData.toolkits[compatibleToolkitId];
const compatibleCategory = categories[compatibleToolkit?.category_id];
return `
<tr data-record-id="${recordId}" data-toolkit-id="${compatibleToolkitId}">
<td>
<strong>${compatibleToolkit?.title || 'Неизвестный инструмент'}</strong>
${compatibleToolkit ? `
<button class="btn btn-sm btn-link p-0 ms-1 view-toolkit-btn"
data-toolkit-id="${compatibleToolkitId}"
title="Просмотреть детали">
<i class="bi bi-eye"></i>
</button>
` : ''}
</td>
<td>${compatibleCategory?.title || 'Неизвестно'}</td>
<td><small class="text-muted">${compatibleToolkit?.comment_text || 'Нет комментария'}</small></td>
${accessData.tools_edit ? `
<td>
<button class="btn btn-sm btn-outline-danger delete-compatibility-btn"
data-record-id="${recordId}"
data-toolkit-id="${compatibleToolkitId}"
title="Удалить совместимость">
<i class="bi bi-trash"></i>
</button>
</td>
` : ''}
</tr>
`;
}).join('')}
</tbody>
</table>
</div>
`;
} else {
compatibilityHtml = `
<div class="alert alert-info mb-3">
<i class="bi bi-info-circle me-2"></i>
Нет данных о совместимых инструментах
</div>
`;
}
// Вставляем 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 = `
<div class="modal-dialog modal-lg">
<div class="modal-content modal-content-green">
<div class="modal-header">
<h5 class="modal-title">Добавить совместимый инструмент</h5>
<button type="button"
class="btn-close btn-close"
data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<input type="text" class="form-control" id="compatibilitySearch"
placeholder="Поиск инструмента по названию...">
</div>
<div class="table-responsive" style="max-height: 400px; overflow-y: auto;">
<table class="table table-sm table-hover">
<thead>
<tr>
<th width="40"></th>
<th>Название</th>
<th>Категория</th>
<th>Описание</th>
</tr>
</thead>
<tbody id="compatibilityToolkitsList">
${filteredToolkits.map(toolkit => {
const category = categories[toolkit.category_id];
return `
<tr data-toolkit-id="${toolkit.id}"
data-toolkit-title="${toolkit.title.toLowerCase()}">
<td>
<button class="btn btn-sm btn-outline-primary select-compatibility-btn"
data-toolkit-id="${toolkit.id}">
<i class="bi bi-plus"></i>
</button>
</td>
<td><strong>${toolkit.title}</strong></td>
<td>${category?.title || 'Неизвестно'}</td>
<td><small class="text-muted">${toolkit.description || 'Нет описания'}</small></td>
</tr>
`;
}).join('')}
</tbody>
</table>
</div>
${filteredToolkits.length === 0 ? `
<div class="alert alert-info">
<i class="bi bi-info-circle me-2"></i>
Нет доступных инструментов для добавления
</div>
` : ''}
</div>
<div class="modal-footer bg-success text-white">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">
Отмена
</button>
</div>
</div>
</div>
`;
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();
Binary file not shown.
Binary file not shown.
+9
View File
@@ -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)}"}
+43 -8
View File
@@ -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)
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
Binary file not shown.
+147 -2
View File
@@ -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):