работа с перемещением и списанием

This commit is contained in:
2025-12-01 22:32:57 +03:00
parent dc85e7c0c9
commit f3213c696f
6 changed files with 404 additions and 13 deletions
+276
View File
@@ -0,0 +1,276 @@
from db.handlers import StockHandler, 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.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,
)
logger.info(
f"Оприходование инструмента {toolkit_id} на складе {toolbox_id} прошло {'успешно' if recorded else 'не успешно'}"
)
return recorded
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
writeDownLIst = []
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']} успешно завершена"
)
if not target_toolbox_id:
writeDownLIst.append(
{
"id": sourceEdit["id"],
"quantity": takeQuantity,
"price": 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']} успешно завершено"
)
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"],
target_placement=target_placement,
)
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 True if target_toolbox_id else writeDownLIst
async def writeDownRequest(
toolkit_id: int,
toolbox_id: int,
quantity: int,
reason: str,
user_id: int,
price: int = 0,
):
logger.info(
f"Запрос на списание инструмента {toolkit_id} со склада {toolbox_id} в количестве {quantity} по цене {price} ..."
)
recorded = await StocksRecordsHandler.add(
action="Списание",
source_stock_id=None,
target_stock_id=None,
source_toolbox_id=toolbox_id,
target_toolbox_id=None,
toolkit_id=toolkit_id,
init_user_id=user_id,
reason=reason,
quantity=quantity,
price=price,
)
logger.info(
f"Запрос на списание инструмента {toolkit_id} со склада {toolbox_id} в количестве {quantity} по цене {price} {'успешно завершен' if recorded else 'завершен с ошибкой'}"
)
return recorded
async def writeDownAcceptance(self, record_id: int, user_id: int):
logger.info(f"Принятие записи о списании инструмента {record_id} ...")
writeDownARecord = await StocksRecordsHandler.getById(record_id, True)
if not writeDownARecord:
return False
if writeDownARecord.action != "Списание":
logger.error(
f"Запись {record_id} не является записью о списании инструмента"
)
return False
if writeDownARecord.accepted_at is not None:
logger.error(f"Запись {record_id} уже была принята")
return False
stocksMovements = await self.moving(
action=writeDownARecord.action,
source_toolbox_id=writeDownARecord.source_toolbox_id,
target_toolbox_id=writeDownARecord.target_toolbox_id,
toolkit_id=writeDownARecord.toolkit_id,
quantity=writeDownARecord.quantity,
user_id=user_id,
reason=writeDownARecord.reason,
)
if not stocksMovements:
logger.error(f"Ошибка при списании инструмента")
return False
accept = await StocksRecordsHandler.accept(
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=writeDownARecord.action,
source_stock_id=stock.get("id"),
target_stock_id=None,
source_toolbox_id=writeDownARecord.source_toolbox_id,
target_toolbox_id=writeDownARecord.target_toolbox_id,
toolkit_id=writeDownARecord.toolkit_id,
init_user_id=writeDownARecord.init_user_id,
reason=writeDownARecord.reason,
quantity=stock.get("quantity"),
price=stock.get("price"),
return_record_id=True,
)
if not recorded:
return False
accept = await StocksRecordsHandler.accept(
record_id=recorded,
accept_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))} о списании инструмента успешно приняты {user_id}"
)
return True
# TODO Дописать возврат на склад
async def initialize():
pass
+87 -6
View File
@@ -1,47 +1,64 @@
from datetime import datetime, timedelta
from sqlalchemy import select
from db.schemas import StocksRecords, ServicesRecords
from utils import logger
class StocksRecordsHandler:
async def add(
action: str,
source_toolbox_id: int,
target_toolbox_id: int,
source_stock_id: int,
target_stock_id: int,
toolkit_id: int,
init_user_id: int,
reason: str,
quantity: int,
price: float,
source_toolbox_id: int = None,
target_toolbox_id: int = None,
return_record_id: bool = False,
):
recordData = {
"action": action,
"source_stock_id": source_stock_id,
"target_stock_id": target_stock_id,
"source_toolbox_id": source_toolbox_id,
"target_toolbox_id": target_toolbox_id,
"toolkit_id": toolkit_id,
"init_user_id": init_user_id,
"reason": reason,
"quantity": quantity,
"price": price,
}
if target_toolbox_id:
recordData["target_toolbox_id"] = target_toolbox_id
try:
logger.info(f"Создание записи: {action} от {init_user_id}")
logger.debug(recordData)
record = StocksRecords(**recordData)
await record.save()
logger.info(f"Запись успешно создана, id: {record.id}")
return True
return True if not return_record_id else record.id
except Exception as e:
logger.error(f"Ошибка создания записи: {str(e)}")
return False
async def accept(record_id: int, accept_user_id: int):
async def accept(
record_id: int,
accept_user_id: int,
source_stock_id: int,
quantity: int,
price: float,
):
try:
logger.info(f"Принятие записи {record_id} от {accept_user_id}")
record = await StocksRecords.get(id=record_id)
record.accept_user_id = accept_user_id
record.accepted_at = datetime.now()
record.source_stock_id = source_stock_id
record.quantity = quantity
record.price = price
await record.save()
logger.info(
f"Запись {record_id} успешно принята {accept_user_id} в {record.accepted_at.strftime('%Y-%m-%d %H:%M:%S')}"
@@ -52,11 +69,58 @@ class StocksRecordsHandler:
return False
async def edit(record_id: int, edit_user_id: int, **kwargs):
def updateStockRecord(recordDB, editData):
whatchList = ["toolkit_id", "quantity", "price", "placement"]
for key in whatchList:
if key in editData:
setattr(recordDB, key, editData[key])
return recordDB
try:
from db.handlers.stock import StockHandler
logger.info(f"Обновление записи {record_id} от {edit_user_id}")
record = await StocksRecords.get(id=record_id)
record.edit_user_id = edit_user_id
record.edited_at = datetime.now()
logger.info("Вносим изменения в записи о движении инструмента")
if record.source_stock_id:
logger.info(
"Вносим изменения в записи о движении инструмента из исходного склада"
)
sourceStockRecord = await StockHandler.get(
record.source_stock_id, record=True
)
if "source_toolbox_id" in kwargs:
sourceStockRecord.toolbox_id = kwargs["source_toolbox_id"]
sourceStockRecord = updateStockRecord(sourceStockRecord, kwargs)
await sourceStockRecord.save()
logger.info(
"Внесли изменения в записи о движении инструмента из исходного склада"
)
if record.target_stock_id:
logger.info(
"Вносим изменения в записи о движении инструмента в целевой склад"
)
targetStockRecord = await StockHandler.get(
record.target_stock_id, record=True
)
if "target_toolbox_id" in kwargs:
targetStockRecord.toolbox_id = kwargs["target_toolbox_id"]
targetStockRecord = updateStockRecord(targetStockRecord, kwargs)
await targetStockRecord.save()
logger.info(
"Внесли изменения в записи о движении инструмента в целевой склад"
)
logger.info("Внесли изменения в записи о движении инструмента")
edited = {}
for key, value in kwargs.items():
originalValue = getattr(record, key)
@@ -69,6 +133,7 @@ class StocksRecordsHandler:
)
logger.debug(edited)
return True
except Exception as e:
logger.error(f"Ошибка обновления записи: {str(e)}")
return False
@@ -103,6 +168,22 @@ class StocksRecordsHandler:
logger.error(f"Ошибка получения записей: {str(e)}")
return False
async def getById(record_id: int, record: bool = False):
from db import CRUD
try:
logger.info(f"Получение записи {record_id}")
query = select(StocksRecords).where(StocksRecords.id == record_id)
stocksRecord = await CRUD.read(query)
if not stocksRecord:
logger.info(f"Запись {record_id} не найдена")
return False
logger.info(f"Запись {record_id} успешно получена")
return stocksRecord.toDict() if not record else stocksRecord
except Exception as e:
logger.error(f"Ошибка получения записи: {str(e)}")
return False
class ServiceRecordsHandler:
async def add(user_id: int, details: dict):
+31 -4
View File
@@ -27,7 +27,7 @@ def filterQuantity(stocksData, filtered):
class StockHandler:
async def add(**kwargs):
async def add(**kwargs) -> dict:
newStock = await Stock(**kwargs).save()
logger.info(
f"Новая запись об инструменте {newStock.toolkit_data.title} на складе {newStock.toolbox_data.title} успешно создана"
@@ -40,14 +40,14 @@ class StockHandler:
stocks = await CRUD.read(select(Stock), all=True)
return filterQuantity(stocks, filtered)
async def get(stockId: int, filtered: bool = True):
async def get(stockId: int, filtered: bool = True, record: bool = False):
from db import CRUD
stock = await CRUD.read(select(Stock).where(Stock.id == stockId))
if not stock:
logger.error("Запись об остатках не найдена")
return {}
return filterQuantity(stock, filtered)
return filterQuantity(stock, filtered) if not record else stock
async def getByToolboxId(toolboxId: int, filtered: bool = True):
from db import CRUD
@@ -63,7 +63,34 @@ class StockHandler:
stocks = await CRUD.read(query, True)
return filterQuantity(stocks, filtered)
async def edit(stockId: int, **kwargs):
async def getByToolboxIdAndToolkitId(
toolboxId: int, toolkitId: int, filtered: bool = True
) -> list[dict]:
from db import CRUD
query = (
select(Stock)
.where(Stock.toolbox_id == toolboxId)
.where(Stock.toolkit_id == toolkitId)
)
stocks = await CRUD.read(query, True)
return filterQuantity(stocks, filtered)
async def getByToolboxIdAndToolkitIdAndQPrice(
toolboxId: int, toolkitId: int, price: float, filtered: bool = True
) -> list[dict]:
from db import CRUD
query = (
select(Stock)
.where(Stock.toolbox_id == toolboxId)
.where(Stock.toolkit_id == toolkitId)
.where(Stock.price == price)
)
stocks = await CRUD.read(query, True)
return filterQuantity(stocks, filtered)
async def edit(stockId: int, **kwargs) -> dict:
from db import CRUD
stock = await CRUD.read(select(Stock).where(Stock.id == stockId))
+9 -2
View File
@@ -1,5 +1,5 @@
from datetime import datetime
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, Text
from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer, String, Text
from sqlalchemy.dialects.postgresql import JSONB
from db import Base
import utils
@@ -9,9 +9,15 @@ class StocksRecords(Base):
__tablename__ = "stocks_records"
id = Column(Integer, primary_key=True, index=True)
source_stock_id = Column(
Integer, ForeignKey("stocks.id", ondelete="CASCADE"), nullable=True
)
target_stock_id = Column(
Integer, ForeignKey("stocks.id", ondelete="CASCADE"), nullable=True
)
action = Column(String, nullable=False)
source_toolbox_id = Column(
Integer, ForeignKey("toolboxes.id", ondelete="CASCADE"), nullable=False
Integer, ForeignKey("toolboxes.id", ondelete="CASCADE"), nullable=True
)
target_toolbox_id = Column(
Integer, ForeignKey("toolboxes.id", ondelete="CASCADE"), nullable=True
@@ -27,6 +33,7 @@ class StocksRecords(Base):
)
reason = Column(Text, nullable=False)
quantity = Column(Integer, nullable=False)
price = Column(Float, nullable=False)
edit_user_id = Column(
Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True
)
+1 -1
View File
@@ -2,4 +2,4 @@ from .loggers import *
from .for_DB import *
from .password import *
from .image import *
from .safe_filemane import *
from .safe_filename import *