import random from db.handlers.stock import StockHandler from db.handlers.toolbox import ToolboxHandler from db.handlers.toolkit import ToolkitHandler from db.handlers.user import UserHandler from db.handlers.access import AccessLevelHandler from db.handlers.records import StocksRecordsHandler from utils import logger class StocksActions: async def registration( toolkit_id: int, toolbox_id: int, user_id: int, quantity: int, price: int, placement: str, reason: str, ): logger.info( f"Оприходование инструмента {toolkit_id} на складе {toolbox_id} ..." ) newStocks = await StockHandler.add( toolkit_id=toolkit_id, toolbox_id=toolbox_id, quantity=quantity, price=price, placement=placement, ) if newStocks is None: logger.error( f"Оприходование инструмента {toolkit_id} на складе {toolbox_id} не удалось" ) return False recorded = await StocksRecordsHandler.add( action="Оприходование", source_stock_id=None, target_stock_id=newStocks.get("id"), source_toolbox_id=None, target_toolbox_id=toolbox_id, toolkit_id=toolkit_id, init_user_id=user_id, reason=reason, quantity=quantity, price=price, return_record_id=True, ) logger.info( f"Оприходование инструмента {toolkit_id} на складе {toolbox_id} прошло {'успешно' if recorded else 'не успешно'}" ) if recorded: accepted = await StocksRecordsHandler.decide( recorded, user_id, None, quantity, price ) if not accepted: logger.error( f"Принятие записи о оприходовании инструмента {toolkit_id} на складе {toolbox_id} не удалось" ) return accepted return False async def moving( action: str, source_toolbox_id: int, target_toolbox_id: int, toolkit_id: int, quantity: int, user_id: int, reason: str, target_placement: str = None, ): logger.info( f"{action} инструмента {toolkit_id} со склада {source_toolbox_id} на склад {target_toolbox_id} ..." ) availability = await StockHandler.getByToolboxIdAndToolkitId( source_toolbox_id, toolkit_id ) if len(availability) == 0: logger.error(f"На складе {source_toolbox_id} нет инструмента {toolkit_id}") return False totalQuantity = sum([stock["quantity"] for stock in availability]) if quantity > totalQuantity: logger.error( f"На складе {source_toolbox_id} нет в количестве {quantity} инструмента {toolkit_id}" ) return False totalTakeQuantity = 0 movementsList = [] for stock in availability: if quantity == totalTakeQuantity: break takeQuantity = min(stock["quantity"], quantity - totalTakeQuantity) totalTakeQuantity += takeQuantity logger.info( f"Списание инструмента {toolkit_id} со склада {source_toolbox_id} на склад {target_toolbox_id} в количестве {takeQuantity} по цене {stock['price']} ..." ) sourceEdit = await StockHandler.edit( stock["id"], quantity=stock["quantity"] - takeQuantity ) if not sourceEdit: logger.error( f"Ошибка обновления записи об остатках инструмента {toolkit_id} на складе {source_toolbox_id}" ) return False logger.info( f"Списание инструмента {toolkit_id} со склада {source_toolbox_id} на склад {target_toolbox_id} в количестве {takeQuantity} по цене {stock['price']} успешно завершена" ) movementsList.append( { "id": sourceEdit["id"], "quantity": takeQuantity, "price": stock["price"], } ) if not target_toolbox_id: continue existing = await StockHandler.getByToolboxIdAndToolkitIdAndQPrice( target_toolbox_id, toolkit_id, stock["price"] ) if len(existing) == 0: logger.info( f"Добавление инструмента {toolkit_id} на склад {target_toolbox_id} по цене {stock['price']} ..." ) targetStock = await StockHandler.add( toolkit_id=toolkit_id, toolbox_id=target_toolbox_id, quantity=takeQuantity, price=stock["price"], placement=target_placement, ) if not targetStock: logger.error( f"Ошибка добавления инструмента {toolkit_id} на склад {target_toolbox_id}" ) return False logger.info( f"Добавление инструмента {toolkit_id} на склад {target_toolbox_id} по цене {stock['price']} успешно завершено" ) else: logger.info( f"Изменение остатков инструмента {toolkit_id} на склад {target_toolbox_id} по цене {stock['price']} ..." ) targetStock = await StockHandler.edit( existing[0]["id"], quantity=existing[0]["quantity"] + takeQuantity, ) if not targetStock: logger.error( f"Ошибка обновления записи об остатках инструмента {toolkit_id} на складе {target_toolbox_id}" ) return False logger.info( f"Изменение остатков инструмента {toolkit_id} на склад {target_toolbox_id} по цене {stock['price']} успешно завершено" ) recorded = await StocksRecordsHandler.add( action=action, source_stock_id=stock["id"], target_stock_id=targetStock.get("id"), source_toolbox_id=source_toolbox_id, target_toolbox_id=target_toolbox_id, toolkit_id=toolkit_id, init_user_id=user_id, reason=reason, quantity=quantity, price=stock["price"], ) if not recorded: logger.error( f"Ошибка создания записи о {action} инструмента {toolkit_id} со склада {source_toolbox_id} на склад {target_toolbox_id}" ) return False logger.info( f"{action} инструмента {toolkit_id} со склада {source_toolbox_id} на склад {target_toolbox_id} прошло успешно" ) return movementsList async def movingRequest( action: str, toolkit_id: int, source_toolbox_id: int, target_toolbox_id: int, quantity: int, reason: str, user_id: int, price: int = 0, return_record_id: bool = False, ): logger.info( f"Запрос на {action} инструмента {toolkit_id} со склада {source_toolbox_id} в количестве {quantity} по цене {price} ..." ) recorded = await StocksRecordsHandler.add( action=action, source_stock_id=None, target_stock_id=None, source_toolbox_id=source_toolbox_id, target_toolbox_id=target_toolbox_id, toolkit_id=toolkit_id, init_user_id=user_id, reason=reason, quantity=quantity, price=price, return_record_id=return_record_id, ) logger.info( f"Запрос на {action} инструмента {toolkit_id} со склада {source_toolbox_id} в количестве {quantity} по цене {price} {'успешно завершен' if recorded else 'завершен с ошибкой'}" ) return recorded async def movingDecision(record_id: int, user_id: int, accepted: bool = True): logger.info( f"{'Принятие' if accepted else 'Отклонение'} записи о движении инструмента {record_id} ..." ) movingRecord = await StocksRecordsHandler.getById(record_id, True) if not movingRecord: logger.error(f"Запись {record_id} не найдена") return False if movingRecord.accepted is not None: logger.error( f"Запись {record_id} уже была {'принята' if movingRecord.accepted else 'отклонена'}" ) return False if not accepted: return await StocksRecordsHandler.decide( record_id, user_id, movingRecord.source_stock_id, movingRecord.quantity, movingRecord.price, accepted, ) target_toolbox_id = ( movingRecord.target_toolbox_id if movingRecord.action != "Списание" else None ) logger.warning(f"{target_toolbox_id = }, {movingRecord.action = }") stocksMovements = await StocksActions.moving( action=movingRecord.action, source_toolbox_id=movingRecord.source_toolbox_id, target_toolbox_id=target_toolbox_id, toolkit_id=movingRecord.toolkit_id, quantity=movingRecord.quantity, user_id=user_id, reason=movingRecord.reason, ) if not stocksMovements: logger.error(f"Ошибка при {movingRecord.action} инструмента") return False accept = await StocksRecordsHandler.decide( record_id, user_id, stocksMovements[0].get("id"), stocksMovements[0].get("quantity"), stocksMovements[0].get("price"), ) if not accept: return accept totalRecordsIds = [record_id] if len(stocksMovements) > 1: for stock in stocksMovements[1:]: recorded = await StocksRecordsHandler.add( action=movingRecord.action, source_stock_id=stock.get("id"), target_stock_id=None, source_toolbox_id=movingRecord.source_toolbox_id, target_toolbox_id=target_toolbox_id, toolkit_id=movingRecord.toolkit_id, init_user_id=movingRecord.init_user_id, reason=movingRecord.reason, quantity=stock.get("quantity"), price=stock.get("price"), return_record_id=True, ) if not recorded: return False accept = await StocksRecordsHandler.decide( record_id=recorded, decision_user_id=user_id, source_stock_id=stock.get("id"), quantity=stock.get("quantity"), price=stock.get("price"), ) if not accept: return False totalRecordsIds.append(recorded) logger.info( f"Записи {', '.join(map(str, totalRecordsIds))} о {movingRecord.action} инструмента успешно приняты {user_id}" ) return True async def takeToolkit( source_toolbox_id: int, target_toolbox_id: int, toolkit_id: int, quantity: int, reason: str, user_id: int, price: int = 0, ): logger.info( f"Формирование запроса на получение инструмента {toolkit_id} на склад {target_toolbox_id} со склада {source_toolbox_id} в количестве {quantity} ..." ) takeRequest = await StocksActions.movingRequest( action="Получение", source_toolbox_id=source_toolbox_id, target_toolbox_id=target_toolbox_id, toolkit_id=toolkit_id, quantity=quantity, user_id=user_id, reason=reason, price=price, return_record_id=True, ) if not takeRequest: logger.error( f"Формирование запроса на получение инструмента {toolkit_id} не удалось" ) return False logger.info( f"Формирование запроса на получение инструмента {toolkit_id} успешно завершено" ) logger.info( f"Принятие запроса {takeRequest} на получение инструмента {toolkit_id} ..." ) accepted = await StocksActions.movingDecision(takeRequest, user_id) if not accepted: logger.error( f"Принятие запроса {takeRequest} на получение инструмента {toolkit_id} не удалось" ) return False logger.info( f"Принятие запроса {takeRequest} на получение инструмента {toolkit_id} успешно завершено" ) return True async def initialize(): toolboxes = await ToolboxHandler.getAll() toolboxesDict = { toolbox.get("title"): toolbox.get("id") for toolbox in toolboxes } toolboxesOwners = { toolbox.get("owner_id"): toolbox.get("id") for toolbox in toolboxes if toolbox.get("owner_id") } users = await UserHandler.getAll() usersDict = {user.get("login"): user.get("id") for user in users} toolkits = await ToolkitHandler.getAll() logger.warning("Наполнение складов ...") for toolkit in toolkits: if "Сверло" in toolkit.get("title"): toolboxId = toolboxesDict.get("Шкаф") else: toolboxId = toolboxesDict.get("Стеллаж") placement = chr(65 + random.randint(0, 25)) + str(random.randint(1, 19)) registryCount = 5 logger.warning( f">> Наполнение склада {toolboxId} инструментом {toolkit.get('title')}" ) for i in range(registryCount): quantity = random.randint(20, 30) price = round(300 + random.random() * 500, 2) logger.warning(f">>>> Количество: {quantity}, Цена: {price}") success = await StocksActions.registration( toolkit_id=toolkit.get("id"), toolbox_id=toolboxId, user_id=usersDict.get("storekeeper"), quantity=quantity, price=price, placement=placement, reason=f"Приход инструмента {toolkit.get('title')}. Счёт-фактура № {random.randint(1000, 10000)}-{i+1}", ) if not success: logger.error(f"Приход инструмента {toolkit.get('title')} не удался") return False logger.warning("Наполнение складов завершено") logger.warning("Получение инструментов из общих складов ...") accessLevels = await AccessLevelHandler.getAll() accessLevelsDict = { accessLevel.get("id"): accessLevel.get("available_own_toolbox") for accessLevel in accessLevels } users = await UserHandler.getAll() usersDict = {user.get("login"): user.get("id") for user in users} for user in users: userAccessLevelId = user.get("access_level_id") if not accessLevelsDict.get(userAccessLevelId): continue own_toolbox_id = toolboxesOwners.get(user.get("id")) logger.warning( f"Получение инструментов из общего склада пользователем {user.get('login')} ..." ) for toolkit in toolkits: logger.warning( f">> Выдача инструмента {toolkit.get('title')} пользователю {user.get('login')} ..." ) if "Сверло" in toolkit.get("title"): main_toolbox_id = toolboxesDict.get("Шкаф") else: main_toolbox_id = toolboxesDict.get("Стеллаж") actionsCount = random.randint(3, 5) for i in range(actionsCount): success = await StocksActions.takeToolkit( source_toolbox_id=main_toolbox_id, target_toolbox_id=own_toolbox_id, toolkit_id=toolkit.get("id"), quantity=random.randint(10, 19), reason=f"Получение инструмента {toolkit.get('title')}. Для выполнения заказа № {random.randint(1000, 10000)}", user_id=user.get("id"), ) if not success: logger.error( f"Получение инструмента {toolkit.get('title')} пользователю {user.get('login')} не удалось" ) return False logger.warning( f">>>> Получение инструмента {toolkit.get('title')} пользователю {user.get('login')} успешно завершено" ) logger.warning("Получение инструментов из общих складов завершено") logger.warning("Направление запросов на возврат и списание инструментов ...") requestsDict = {"Списание": [], "Возврат": []} for user in users: userAccessLevelId = user.get("access_level_id") if not accessLevelsDict.get(userAccessLevelId): continue own_toolbox_id = toolboxesOwners.get(user.get("id")) logger.warning( f"Направление запросов на возврат и списание инструмента от пользователя {user.get('login')} ..." ) for action in requestsDict.keys(): for toolkit in toolkits: if action == "Списание": main_toolbox_id = None else: if "Сверло" in toolkit.get("title"): main_toolbox_id = toolboxesDict.get("Шкаф") else: main_toolbox_id = toolboxesDict.get("Стеллаж") actionsCount = random.randint(1, 3) for i in range(actionsCount): success = await StocksActions.movingRequest( action=action, toolkit_id=toolkit.get("id"), source_toolbox_id=own_toolbox_id, target_toolbox_id=main_toolbox_id, quantity=random.randint(3, 5), reason=f"{action} инструмента {toolkit.get('title')}. После выполнения заказа", user_id=user.get("id"), return_record_id=True, ) if not success: logger.error( f"{action} инструмента {toolkit.get('title')} пользователю {user.get('login')} не удалось" ) return False requestsDict.get(action).append(success) logger.warning( f">>>> {action} инструмента пользователю {user.get('login')} успешно завершено" ) logger.warning( "Направление запросов на возврат и списание инструментов завершено" ) logger.warning("Обработка запросов на возврат и списание инструментов ...") decisionsDict = { "manager": {"accept": [], "reject": [], "ignore": []}, "storekeeper": {"accept": [], "reject": [], "ignore": []}, } for recordsList in requestsDict.values(): managerList, storekeeperList = ( recordsList[: len(recordsList) // 2], recordsList[len(recordsList) // 2 :], ) ( decisionsDict["manager"]["accept"], decisionsDict["manager"]["reject"], decisionsDict["manager"]["ignore"], ) = ( managerList[: len(managerList) // 3], managerList[len(managerList) // 3 : len(managerList) // 3 * 2], managerList[len(managerList) // 3 * 2 :], ) ( decisionsDict["storekeeper"]["accept"], decisionsDict["storekeeper"]["reject"], decisionsDict["storekeeper"]["ignore"], ) = ( storekeeperList[: len(storekeeperList) // 3], storekeeperList[ len(storekeeperList) // 3 : len(storekeeperList) // 3 * 2 ], storekeeperList[len(storekeeperList) // 3 * 2 :], ) for role in decisionsDict.keys(): user_id = usersDict.get(role) for decision in decisionsDict.get(role).keys(): match decision: case "accept": accepted = True case "reject": accepted = False case "ignore": continue for record_id in decisionsDict.get(role).get(decision): success = await StocksActions.movingDecision( record_id=record_id, user_id=user_id, accepted=accepted, ) if not success: logger.error( f"Принятие записи {record_id} пользователем {role} не удалось" ) return False logger.warning( "Обработка запросов на возврат и списание инструментов завершено" ) return True