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

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 datetime import datetime, timedelta
from sqlalchemy import select from sqlalchemy import select
from db.schemas import StocksRecords, ServicesRecords from db.schemas import StocksRecords, ServicesRecords
from utils import logger from utils import logger
class StocksRecordsHandler: class StocksRecordsHandler:
async def add( async def add(
action: str, action: str,
source_toolbox_id: int, source_stock_id: int,
target_toolbox_id: int, target_stock_id: int,
toolkit_id: int, toolkit_id: int,
init_user_id: int, init_user_id: int,
reason: str, reason: str,
quantity: int, quantity: int,
price: float,
source_toolbox_id: int = None,
target_toolbox_id: int = None,
return_record_id: bool = False,
): ):
recordData = { recordData = {
"action": action, "action": action,
"source_stock_id": source_stock_id,
"target_stock_id": target_stock_id,
"source_toolbox_id": source_toolbox_id, "source_toolbox_id": source_toolbox_id,
"target_toolbox_id": target_toolbox_id,
"toolkit_id": toolkit_id, "toolkit_id": toolkit_id,
"init_user_id": init_user_id, "init_user_id": init_user_id,
"reason": reason, "reason": reason,
"quantity": quantity, "quantity": quantity,
"price": price,
} }
if target_toolbox_id:
recordData["target_toolbox_id"] = target_toolbox_id
try: try:
logger.info(f"Создание записи: {action} от {init_user_id}") logger.info(f"Создание записи: {action} от {init_user_id}")
logger.debug(recordData) logger.debug(recordData)
record = StocksRecords(**recordData) record = StocksRecords(**recordData)
await record.save() await record.save()
logger.info(f"Запись успешно создана, id: {record.id}") logger.info(f"Запись успешно создана, id: {record.id}")
return True return True if not return_record_id else record.id
except Exception as e: except Exception as e:
logger.error(f"Ошибка создания записи: {str(e)}") logger.error(f"Ошибка создания записи: {str(e)}")
return False 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: try:
logger.info(f"Принятие записи {record_id} от {accept_user_id}") logger.info(f"Принятие записи {record_id} от {accept_user_id}")
record = await StocksRecords.get(id=record_id) record = await StocksRecords.get(id=record_id)
record.accept_user_id = accept_user_id record.accept_user_id = accept_user_id
record.accepted_at = datetime.now() record.accepted_at = datetime.now()
record.source_stock_id = source_stock_id
record.quantity = quantity
record.price = price
await record.save() await record.save()
logger.info( logger.info(
f"Запись {record_id} успешно принята {accept_user_id} в {record.accepted_at.strftime('%Y-%m-%d %H:%M:%S')}" f"Запись {record_id} успешно принята {accept_user_id} в {record.accepted_at.strftime('%Y-%m-%d %H:%M:%S')}"
@@ -52,11 +69,58 @@ class StocksRecordsHandler:
return False return False
async def edit(record_id: int, edit_user_id: int, **kwargs): 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: try:
from db.handlers.stock import StockHandler
logger.info(f"Обновление записи {record_id} от {edit_user_id}") logger.info(f"Обновление записи {record_id} от {edit_user_id}")
record = await StocksRecords.get(id=record_id) record = await StocksRecords.get(id=record_id)
record.edit_user_id = edit_user_id record.edit_user_id = edit_user_id
record.edited_at = datetime.now() 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 = {} edited = {}
for key, value in kwargs.items(): for key, value in kwargs.items():
originalValue = getattr(record, key) originalValue = getattr(record, key)
@@ -69,6 +133,7 @@ class StocksRecordsHandler:
) )
logger.debug(edited) logger.debug(edited)
return True return True
except Exception as e: except Exception as e:
logger.error(f"Ошибка обновления записи: {str(e)}") logger.error(f"Ошибка обновления записи: {str(e)}")
return False return False
@@ -103,6 +168,22 @@ class StocksRecordsHandler:
logger.error(f"Ошибка получения записей: {str(e)}") logger.error(f"Ошибка получения записей: {str(e)}")
return False 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: class ServiceRecordsHandler:
async def add(user_id: int, details: dict): async def add(user_id: int, details: dict):
+31 -4
View File
@@ -27,7 +27,7 @@ def filterQuantity(stocksData, filtered):
class StockHandler: class StockHandler:
async def add(**kwargs): async def add(**kwargs) -> dict:
newStock = await Stock(**kwargs).save() newStock = await Stock(**kwargs).save()
logger.info( logger.info(
f"Новая запись об инструменте {newStock.toolkit_data.title} на складе {newStock.toolbox_data.title} успешно создана" f"Новая запись об инструменте {newStock.toolkit_data.title} на складе {newStock.toolbox_data.title} успешно создана"
@@ -40,14 +40,14 @@ class StockHandler:
stocks = await CRUD.read(select(Stock), all=True) stocks = await CRUD.read(select(Stock), all=True)
return filterQuantity(stocks, filtered) 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 from db import CRUD
stock = await CRUD.read(select(Stock).where(Stock.id == stockId)) stock = await CRUD.read(select(Stock).where(Stock.id == stockId))
if not stock: if not stock:
logger.error("Запись об остатках не найдена") logger.error("Запись об остатках не найдена")
return {} return {}
return filterQuantity(stock, filtered) return filterQuantity(stock, filtered) if not record else stock
async def getByToolboxId(toolboxId: int, filtered: bool = True): async def getByToolboxId(toolboxId: int, filtered: bool = True):
from db import CRUD from db import CRUD
@@ -63,7 +63,34 @@ class StockHandler:
stocks = await CRUD.read(query, True) stocks = await CRUD.read(query, True)
return filterQuantity(stocks, filtered) 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 from db import CRUD
stock = await CRUD.read(select(Stock).where(Stock.id == stockId)) stock = await CRUD.read(select(Stock).where(Stock.id == stockId))
+9 -2
View File
@@ -1,5 +1,5 @@
from datetime import datetime 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 sqlalchemy.dialects.postgresql import JSONB
from db import Base from db import Base
import utils import utils
@@ -9,9 +9,15 @@ class StocksRecords(Base):
__tablename__ = "stocks_records" __tablename__ = "stocks_records"
id = Column(Integer, primary_key=True, index=True) 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) action = Column(String, nullable=False)
source_toolbox_id = Column( source_toolbox_id = Column(
Integer, ForeignKey("toolboxes.id", ondelete="CASCADE"), nullable=False Integer, ForeignKey("toolboxes.id", ondelete="CASCADE"), nullable=True
) )
target_toolbox_id = Column( target_toolbox_id = Column(
Integer, ForeignKey("toolboxes.id", ondelete="CASCADE"), nullable=True Integer, ForeignKey("toolboxes.id", ondelete="CASCADE"), nullable=True
@@ -27,6 +33,7 @@ class StocksRecords(Base):
) )
reason = Column(Text, nullable=False) reason = Column(Text, nullable=False)
quantity = Column(Integer, nullable=False) quantity = Column(Integer, nullable=False)
price = Column(Float, nullable=False)
edit_user_id = Column( edit_user_id = Column(
Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True
) )
+1 -1
View File
@@ -2,4 +2,4 @@ from .loggers import *
from .for_DB import * from .for_DB import *
from .password import * from .password import *
from .image import * from .image import *
from .safe_filemane import * from .safe_filename import *