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, None, 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)
 |