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 db.schemas.records import StocksRecords 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=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} не удалось" ) refillUpdated = await ToolkitHandler.updateRefillDate(toolkit_id) if not refillUpdated: logger.error( f"Обновление даты последнего заполнения инструмента {toolkit_id} не удалось" ) return accepted return False async def moving( action: str, source_toolbox_id: int, target_toolbox_id: int, toolkit_id: int, quantity: int, 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: logger.warning( f"Производится списание инструмента {toolkit_id} со склада {source_toolbox_id} в количестве {takeQuantity} по цене {stock['price']}" ) 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']} успешно завершено" ) logger.info( f"{action} инструмента {toolkit_id} со склада {source_toolbox_id} на склад {target_toolbox_id} прошло успешно" ) moveUpdated = await ToolkitHandler.updateMovindDate(toolkit_id) if not moveUpdated: logger.error(f"Ошибка обновления даты движения инструмента {toolkit_id}") return False 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: 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=return_record, ) 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, record: StocksRecords = None, ): logger.info( f"{'Принятие' if accepted else 'Отклонение'} записи {record_id if not record else record.id} о движении инструмента {'' if not record else record.toolkit_id} ..." ) movingRecord = ( await StocksRecordsHandler.getById(record_id, True) if not record else record ) 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( movingRecord, 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, ) if not stocksMovements: logger.error(f"Ошибка при {movingRecord.action} инструмента") return False accept = await StocksRecordsHandler.decide( movingRecord, 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=True, ) if not recorded: return False accept = await StocksRecordsHandler.decide( record=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=True, ) if not takeRequest: logger.error( f"Формирование запроса на получение инструмента {toolkit_id} не удалось" ) return False logger.info( f"Формирование запроса на получение инструмента {toolkit_id} успешно завершено" ) logger.info( f"Принятие запроса {takeRequest.id} на получение инструмента {toolkit_id} ..." ) accepted = await StocksActions.movingDecision(None, user_id, record=takeRequest) if not accepted: logger.error( f"Принятие запроса {takeRequest.id} на получение инструмента {toolkit_id} не удалось" ) return False logger.info( f"Принятие запроса {takeRequest.id} на получение инструмента {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("Шкаф") placement = None 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=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": []}, } logger.info("Разбивка списоков запросов ...") 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 :], ) logger.warning("Применение решений ...") for role in decisionsDict.keys(): logger.warning(f"Принятие записей пользователем {role} ...") user_id = usersDict.get(role) for decision in decisionsDict.get(role).keys(): logger.warning(f"Принятие решения {decision} пользователем {role} ...") match decision: case "accept": accepted = True case "reject": accepted = False case "ignore": continue for record in decisionsDict.get(role).get(decision): logger.info( f"{'Принятие' if accepted else 'Отклонение'} записи {record.id} пользователем {role} ... " ) success = await StocksActions.movingDecision( record_id=None, user_id=user_id, accepted=accepted, record=record, ) if not success: logger.error( f"{'Принятие' if accepted else 'Отклонение'} записи {record.id} пользователем {role} не удалось" ) return False logger.warning( "Обработка запросов на возврат и списание инструментов завершено" ) return True