from datetime import datetime from db.handlers.stock import StockHandler from utils import logger, saveImage, safeFilename from db import CRUD from db.schemas.toolkit import Toolkit from sqlalchemy import select from db.handlers.records import ServiceRecordsHandler from utils.image import deleteImage def handleToolkitImage(imageData, title: str): import base64 title = safeFilename(title) fileName = f"static/images/tools/{title}.png" if imageData.startswith("data:image"): header, encoded = imageData.split(",", 1) else: encoded = imageData file_bytes = base64.b64decode(encoded) if not saveImage(file_bytes, fileName): return None return fileName class ToolkitHandler: async def add(toolkitData: dict, user_id: int = None): title = toolkitData.get("title", None) if not title: logger.error("Не указано название инструмента") return {"errorMessage": "Не указано название инструмента"} query = select(Toolkit).where(Toolkit.title == title) toolkit = await CRUD.read(query) if toolkit: logger.error("Инструмент с таким названием уже существует") return {"errorMessage": "Инструмент с таким названием уже существует"} try: imageDict = {"main": "static/images/tools/default.png", "additional": []} if "image" in toolkitData: imageData = toolkitData.pop("image") mainImage = imageData.get("main") if mainImage.startswith("static/images/"): imageDict["main"] = mainImage else: imageFileName = handleToolkitImage(mainImage, title) if imageFileName: imageDict["main"] = imageFileName additionalImages = imageData.get("additional", []) if len(additionalImages) > 0: for image in additionalImages: if image.startswith("static/images/"): imageDict["additional"].append(image) else: imageFileName = handleToolkitImage(image, title) if imageFileName: imageDict["additional"].append(imageFileName) toolkitData["image"] = imageDict newToolkit = await Toolkit(**toolkitData).save() except Exception as e: logger.error(f"Ошибка сохранения инструмента: {str(e)}") return {"errorMessage": f"Ошибка сохранения инструмента: {str(e)}"} if not newToolkit: logger.error("Инструмент не сохранен") return {"errorMessage": "Инструмент не сохранен"} logger.info(f"Инструмент {newToolkit.title} успешно создан") await ServiceRecordsHandler.add(user_id, {"Добавлен инструмент": toolkitData}) return newToolkit.toDict() async def updateMovindDate(toolkitId: int): toolkit = await CRUD.read(select(Toolkit).where(Toolkit.id == toolkitId)) if not toolkit: logger.error("Инструмент не найден") return False editedToolkit = await toolkit.edit(moved_at=datetime.now()) if not editedToolkit: logger.error("Инструмент не обновлен") return False return True async def updateRefillDate(toolkitId: int): logger.info(f"Обновление даты пополнения инструмента {toolkitId}...") toolkit = await CRUD.read(select(Toolkit).where(Toolkit.id == toolkitId)) if not toolkit: logger.error("Инструмент не найден") return False editedToolkit = await toolkit.edit(refilled_at=datetime.now()) if not editedToolkit: logger.error("Инструмент не обновлен") return False return True async def hideToolkit(userId: int, toolkitId: int, hidden: bool = True): logger.info( f"{'Скрытие' if hidden else 'Отображение'} инструмента {toolkitId}..." ) return await ToolkitHandler.edit(userId, id=toolkitId, hidden=hidden) async def edit(user_id: int, **kwargs): title = kwargs.get("title", None) toolkitId = kwargs.pop("id") if title: query = select(Toolkit).where(Toolkit.title == title) toolkit = await CRUD.read(query) if toolkit: if toolkit.id != toolkitId: logger.error("Инструмент с таким названием уже существует") return { "errorMessage": "Инструмент с таким названием уже существует" } query = select(Toolkit).where(Toolkit.id == toolkitId) toolkit = await CRUD.read(query) if not toolkit: logger.error("Инструмент не найден") return {"errorMessage": "Инструмент не найден"} try: if "image" in kwargs: title = kwargs.get("title", toolkit.title) imageData = kwargs.pop("image") imageDict = {"main": "", "additional": []} if imageData.get("main").startswith("static/images/"): imageDict["main"] = imageData.get("main") else: imageFileName = handleToolkitImage(imageData.get("main"), title) if imageFileName: imageDict["main"] = imageFileName deleteImage(toolkit.image.get("main")) for image in imageData.get("additional"): if image.startswith("static/images/"): imageDict["additional"].append(image) else: imageFileName = handleToolkitImage(image, title) if imageFileName: imageDict["additional"].append(imageFileName) for existImage in toolkit.image.get("additional"): if existImage not in imageDict.get("additional"): deleteImage(existImage) kwargs["image"] = imageDict logger.debug(f"Обновление инструмента {toolkit.title}...") editedToolkit = await toolkit.edit(**kwargs) except Exception as e: logger.error(f"Ошибка обновления инструмента: {str(e)}") return {"errorMessage": f"Ошибка обновления инструмента: {str(e)}"} if not editedToolkit: logger.error("Инструмент не обновлен") return {"errorMessage": "Инструмент не обновлен"} logger.info( f"Инструмент {editedToolkit.title} успешно обновлен, изменены данные: {kwargs.keys()}" ) await ServiceRecordsHandler.add( user_id, {f"Обновлен инструмент": editedToolkit.toDict()} ) return editedToolkit.toDict() async def getAll(): query = select(Toolkit) toolkits = await CRUD.read(query, True) return [toolkit.toDict() for toolkit in toolkits] if toolkits else [] async def get(toolkitId: int): query = select(Toolkit).where(Toolkit.id == toolkitId) toolkit = await CRUD.read(query) if not toolkit: logger.error("Инструмент не найден") return {} return toolkit.toDict() async def getSeveral(toolkitIds: list[int]) -> list[dict]: query = select(Toolkit).where(Toolkit.id.in_(toolkitIds)) toolkits = await CRUD.read(query, True) return [toolkit.toDict() for toolkit in toolkits] if toolkits else [] async def checkCatogoryUse(category_id: int): query = select(Toolkit).where(Toolkit.category_id == category_id) toolkit = await CRUD.read(query) return True if toolkit else False async def delete(toolkitId: int, user_id: int = None): movements = await StockHandler.checkToolkitExists(toolkitId) if movements: logger.error("По инструменту было движение") return {"errorMessage": "По инструменту было движение"} query = select(Toolkit).where(Toolkit.id == toolkitId) toolkit = await CRUD.read(query) if not toolkit: logger.error("Инструмент не найден") return {"errorMessage": "Инструмент не найден"} try: toolkitTitle = toolkit.title result = await CRUD.delete(toolkit) except Exception as e: logger.error(f"Ошибка удаления инструмента: {str(e)}") return {"errorMessage": f"Ошибка удаления инструмента: {str(e)}"} logger.info( f"Инструмент {toolkitTitle} {'успешно удален' if result else 'не удален'}" ) await ServiceRecordsHandler.add( user_id, {"Удален инструмент": f"Название: {toolkitTitle}"} ) return {"status": "ok"} if result else {"errorMessage": "Инструмент не удален"} async def initialize(): from .categories import CategoryHandler logger.info("Инициализация инструментов") categoriesList = await CategoryHandler.getAll() categories = {category["title"]: category["id"] for category in categoriesList} baseToolkits = [ { "title": "Фреза №1", "description": "Фреза такая сякая этакая #1", "specifications": { "Диаметр": "10", "Длина": "20", "Ещё что-то": "Ещё столько-то", }, "category_id": categories["Фрезеровка"], "quantity_min": 20, "quantity_min_extra": 10, "external_link": "https://nazv.ru", }, { "title": "Фреза №2", "description": "Фреза такая сякая этакая #2", "specifications": { "Диаметр": "10", "Длина": "20", "Ещё что-то": "Ещё столько-то", }, "category_id": categories["Фрезеровка"], "quantity_min": 20, "quantity_min_extra": 10, "external_link": "https://nazv.ru", }, { "title": "Фреза №3", "description": "Фреза такая сякая этакая #3", "specifications": { "Диаметр": "10", "Длина": "20", "Ещё что-то": "Ещё столько-то", }, "category_id": categories["Фрезеровка"], "quantity_min": 20, "quantity_min_extra": 10, "external_link": "https://nazv.ru", }, { "title": "Пластина №1", "description": "Пластина такая сякая этакая #1", "specifications": { "Размер": "10", "Радиус": "0.4", "Ещё что-то": "Ещё столько-то", }, "image": { "main": "static/images/tools/default.png", "additional": [ "static/images/users/default.png", "static/images/logo.png", ], }, "category_id": categories["Токарка"], "quantity_min": 20, "quantity_min_extra": 10, "external_link": "https://nazv.ru", }, { "title": "Пластина №2", "description": "Пластина такая сякая этакая #2", "specifications": { "Размер": "10", "Радиус": "0.4", "Ещё что-то": "Ещё столько-то", }, "category_id": categories["Токарка"], "quantity_min": 20, "quantity_min_extra": 10, "external_link": "https://nazv.ru", }, { "title": "Пластина №3", "description": "Пластина такая сякая этакая #3", "specifications": { "Размер": "10", "Радиус": "0.4", "Ещё что-то": "Ещё столько-то", }, "category_id": categories["Токарка"], "quantity_min": 20, "quantity_min_extra": 10, "external_link": "https://nazv.ru", }, { "title": "Сверло №1", "description": "Сверло такое сякое этакое #1", "specifications": { "Длина": "30", "Диаметр": "5", "Ещё что-то": "Ещё столько-то", }, "category_id": categories["Слесарка"], "quantity_min": 20, "quantity_min_extra": 10, "external_link": "https://nazv.ru", }, { "title": "Сверло №2", "description": "Сверло такое сякое этакое #2", "specifications": { "Длина": "30", "Диаметр": "5", "Ещё что-то": "Ещё столько-то", }, "category_id": categories["Слесарка"], "quantity_min": 20, "quantity_min_extra": 10, "external_link": "https://nazv.ru", }, { "title": "Сверло №3", "description": "Сверло такое сякое этакое #3", "specifications": { "Длина": "30", "Диаметр": "5", "Ещё что-то": "Ещё столько-то", }, "category_id": categories["Слесарка"], "quantity_min": 20, "quantity_min_extra": 10, "external_link": "https://nazv.ru", }, ] for toolkit in baseToolkits: await ToolkitHandler.add(toolkit) logger.info("Базовые инструменты успешно созданы") return