Files
toolbox/db/handlers/actions.py
T
2025-12-16 09:41:01 +03:00

589 lines
25 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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: float,
placement: str,
reason: str,
) -> dict:
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 {"errorMessage": "Приход инструмента не удалось"}
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 (
{"errorMessage": "Приход инструмента не удалось"}
if not accepted
else {}
)
return {"errorMessage": "Приход инструмента не удалось"}
async def moving(
action: str,
source_toolbox_id: int,
target_toolbox_id: int,
toolkit_id: int,
quantity: int,
):
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"],
)
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 not record else [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.id)
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