193 lines
9.2 KiB
Python
193 lines
9.2 KiB
Python
from fastapi import HTTPException
|
||
from sqlalchemy import select
|
||
from sqlalchemy.orm import selectinload, joinedload
|
||
from starlette import status
|
||
|
||
from models import ResidualPallet, ResidualBox, ResidualProduct, Client, Product
|
||
from schemas.residues import *
|
||
from services.base import BaseService
|
||
|
||
|
||
class ResiduesService(BaseService):
|
||
async def _get_pallet_by_id(self, pallet_id: int) -> Optional[ResidualPallet]:
|
||
stmt = (
|
||
select(ResidualPallet)
|
||
.options(
|
||
selectinload(ResidualPallet.boxes)
|
||
.selectinload(ResidualBox.residual_products)
|
||
.selectinload(ResidualProduct.product)
|
||
.noload(Product.barcodes),
|
||
joinedload(ResidualPallet.client),
|
||
selectinload(ResidualPallet.residual_products)
|
||
.selectinload(ResidualProduct.product)
|
||
.noload(Product.barcodes),
|
||
)
|
||
.where(ResidualPallet.id == pallet_id)
|
||
)
|
||
pallet = (await self.session.execute(stmt)).one_or_none()
|
||
return pallet[0] if pallet else None
|
||
|
||
async def get_pallet(self, pallet_id: int) -> GetResidualPalletResponse:
|
||
pallet = await self._get_pallet_by_id(pallet_id)
|
||
if not pallet:
|
||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f'Паллет с ID:{pallet_id} не найден')
|
||
|
||
return GetResidualPalletResponse(pallet=pallet, client_id=pallet.client_id)
|
||
|
||
async def create_pallet(self, request: CreateResidualPalletRequest) -> CreateResidualPalletResponse:
|
||
client = await self.session.get(Client, request.client_id)
|
||
if not client:
|
||
return CreateResidualPalletResponse(ok=False, message=f'Клиент с ID:{request.client_id} не найден')
|
||
|
||
pallet = ResidualPallet(client_id=request.client_id, created_at=datetime.now())
|
||
self.session.add(pallet)
|
||
await self.session.commit()
|
||
return CreateResidualPalletResponse(ok=True, message='Паллет успешно создан')
|
||
|
||
async def delete_pallet(self, pallet_id: int) -> DeleteResidualPalletResponse:
|
||
pallet = await self.session.get(ResidualPallet, pallet_id)
|
||
if not pallet:
|
||
return DeleteResidualPalletResponse(ok=False, message='Паллет не найден')
|
||
|
||
await self.session.delete(pallet)
|
||
await self.session.commit()
|
||
return DeleteResidualPalletResponse(ok=True, message='Паллет успешно удален')
|
||
|
||
async def _get_box_by_id(self, box_id: int) -> Optional[ResidualBox]:
|
||
stmt = (
|
||
select(ResidualBox)
|
||
.options(
|
||
selectinload(ResidualBox.pallet)
|
||
.noload(ResidualPallet.boxes),
|
||
selectinload(ResidualBox.pallet)
|
||
.joinedload(ResidualPallet.client),
|
||
selectinload(ResidualBox.residual_products)
|
||
.selectinload(ResidualProduct.product)
|
||
.noload(Product.barcodes),
|
||
)
|
||
.where(ResidualBox.id == box_id)
|
||
)
|
||
box = (await self.session.execute(stmt)).one_or_none()
|
||
return box[0] if box else None
|
||
|
||
async def get_box(self, box_id: int) -> GetResidualBoxResponse:
|
||
box = await self._get_box_by_id(box_id)
|
||
if not box:
|
||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f'Короб с ID:{box_id} не найден')
|
||
|
||
if box.client_id:
|
||
client_id = box.client_id
|
||
else:
|
||
pallet = await self._get_pallet_by_id(box.pallet_id)
|
||
client_id = pallet.client_id
|
||
|
||
return GetResidualBoxResponse(box=box, client_id=client_id)
|
||
|
||
async def create_box(self, request: CreateResidualBoxRequest) -> CreateResidualBoxResponse:
|
||
if request.client_id:
|
||
client = await self.session.get(Client, request.client_id)
|
||
if not client:
|
||
return CreateResidualBoxResponse(ok=False, message=f'Клиент с ID:{request.client_id} не найден')
|
||
else:
|
||
pallet = await self.session.get(ResidualPallet, request.pallet_id)
|
||
if not pallet:
|
||
return CreateResidualBoxResponse(ok=False, message=f'Паллет с ID:{request.pallet_id} не найден')
|
||
|
||
box = ResidualBox(created_at=datetime.now(), **request.model_dump())
|
||
self.session.add(box)
|
||
await self.session.commit()
|
||
return CreateResidualBoxResponse(ok=True, message='Короб успешно создан')
|
||
|
||
async def delete_box(self, box_id: int) -> DeleteResidualBoxResponse:
|
||
box = await self.session.get(ResidualBox, box_id)
|
||
if not box:
|
||
return DeleteResidualBoxResponse(ok=False, message=f'Короб с ID:{box_id} не найден')
|
||
|
||
await self.session.delete(box)
|
||
await self.session.commit()
|
||
return DeleteResidualBoxResponse(ok=True, message='Короб успешно удален')
|
||
|
||
async def _create_residual_product(
|
||
self,
|
||
obj: ResidualBox | ResidualPallet,
|
||
request: CreateResidualProductRequest,
|
||
) -> tuple[bool, str]:
|
||
try:
|
||
existing_residual = next(p for p in obj.residual_products if p.product_id == request.data.product_id)
|
||
existing_residual.quantity += request.data.quantity
|
||
self.session.add(existing_residual)
|
||
except StopIteration:
|
||
residual_product = ResidualProduct(**request.data.model_dump())
|
||
self.session.add(residual_product)
|
||
await self.session.commit()
|
||
return True, "Товар успешно добавлен"
|
||
|
||
async def create_residual_product(self, request: CreateResidualProductRequest) -> CreateResidualProductResponse:
|
||
if request.data.box_id:
|
||
obj = await self._get_box_by_id(request.data.box_id)
|
||
if not obj:
|
||
return CreateResidualProductResponse(ok=False, message=f'Короб с ID:{request.data.box_id} не найден')
|
||
else:
|
||
obj = await self.session.get(ResidualPallet, request.data.pallet_id)
|
||
if not obj:
|
||
return CreateResidualProductResponse(
|
||
ok=False, message=f'Паллет с ID:{request.data.pallet_id} не найден',
|
||
)
|
||
ok, message = await self._create_residual_product(obj, request)
|
||
return CreateResidualProductResponse(ok=ok, message=message)
|
||
|
||
async def update_residual_product(
|
||
self,
|
||
request: UpdateResidualProductRequest,
|
||
residual_product_id: int
|
||
) -> UpdateResidualProductResponse:
|
||
residual_product = await self.session.get(ResidualProduct, residual_product_id)
|
||
residual_product.product_id = request.data.product_id
|
||
residual_product.quantity = request.data.quantity
|
||
await self.session.commit()
|
||
return UpdateResidualProductResponse(ok=True, message='Запись о товаре на паллете успешно изменена')
|
||
|
||
async def delete_residual_product(self, residual_product_id: int) -> DeleteResidualProductResponse:
|
||
residual_product = await self.session.get(ResidualProduct, residual_product_id)
|
||
if not residual_product:
|
||
return DeleteResidualProductResponse(ok=False, message=f'Запись для данного паллета и товара не найдена')
|
||
|
||
await self.session.delete(residual_product)
|
||
await self.session.commit()
|
||
return DeleteResidualProductResponse(ok=True, message='Запись о товаре на паллете успешно удалена')
|
||
|
||
async def load_receipt(self, request: LoadReceiptRequest) -> LoadReceiptResponse:
|
||
if not await self.session.get(Client, request.client_id):
|
||
return LoadReceiptResponse(ok=False, message=f'Клиент с ID {request.client_id}')
|
||
|
||
await self._load_receipt_boxes(request.boxes, request.client_id)
|
||
await self._load_receipt_pallets(request.pallets, request.client_id)
|
||
await self.session.commit()
|
||
|
||
return LoadReceiptResponse(ok=True, message='Приемка успешно завершена')
|
||
|
||
async def _load_receipt_boxes(self, boxes: list[ReceiptBoxSchema], client_id: int = None, pallet_id: int = None):
|
||
for receipt_box in boxes:
|
||
box = ResidualBox(client_id=client_id, pallet_id=pallet_id, created_at=datetime.now())
|
||
self.session.add(box)
|
||
await self.session.flush()
|
||
await self._load_receipt_products(receipt_box.products, box_id=box.id)
|
||
|
||
async def _load_receipt_pallets(self, pallets: list[ReceiptPalletSchema], client_id: int):
|
||
for receipt_pallet in pallets:
|
||
pallet = ResidualPallet(client_id=client_id, created_at=datetime.now())
|
||
self.session.add(pallet)
|
||
await self.session.flush()
|
||
await self._load_receipt_boxes(receipt_pallet.boxes, pallet.id)
|
||
await self._load_receipt_products(receipt_pallet.products, pallet_id=pallet.id)
|
||
|
||
async def _load_receipt_products(
|
||
self,
|
||
products: list[ReceiptPalletSchema],
|
||
box_id: int = None,
|
||
pallet_id: int = None,
|
||
):
|
||
for receipt_product in products:
|
||
product = ResidualProduct(box_id=box_id, pallet_id=pallet_id, **receipt_product.model_dump())
|
||
self.session.add(product)
|