142 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			142 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
from typing import Optional
 | 
						||
 | 
						||
from sqlalchemy import select, and_, func, exists, or_
 | 
						||
 | 
						||
from models import CardStatus, Card, CardStatusHistory
 | 
						||
from schemas.status import *
 | 
						||
from services.base import BaseService
 | 
						||
 | 
						||
 | 
						||
class StatusService(BaseService):
 | 
						||
    async def _get_statuses_for_board(self, board_id: int) -> list[CardStatus]:
 | 
						||
        stmt = (
 | 
						||
            select(CardStatus)
 | 
						||
            .where(
 | 
						||
                and_(
 | 
						||
                    CardStatus.board_id == board_id,
 | 
						||
                    CardStatus.is_deleted == False,
 | 
						||
                )
 | 
						||
            )
 | 
						||
            .order_by(CardStatus.ordinal_number)
 | 
						||
        )
 | 
						||
        statuses = (await self.session.scalars(stmt)).all()
 | 
						||
        return list(statuses)
 | 
						||
 | 
						||
    async def _get_status_by_id(self, status_id: int) -> Optional[CardStatus]:
 | 
						||
        stmt = (
 | 
						||
            select(CardStatus)
 | 
						||
            .where(CardStatus.id == status_id)
 | 
						||
        )
 | 
						||
        status = await self.session.scalar(stmt)
 | 
						||
        return status
 | 
						||
 | 
						||
    async def create_status(self, request: CreateStatusRequest) -> CreateStatusResponse:
 | 
						||
        statuses = await self._get_statuses_for_board(request.status.board_id)
 | 
						||
        if len(statuses) == 0:
 | 
						||
            ordinal_number = 1
 | 
						||
        else:
 | 
						||
            statuses[-1].is_finishing = False
 | 
						||
            ordinal_number = statuses[-1].ordinal_number + 1
 | 
						||
 | 
						||
        status = CardStatus(
 | 
						||
            **request.status.model_dump(),
 | 
						||
            ordinal_number=ordinal_number,
 | 
						||
            is_finishing=True,
 | 
						||
        )
 | 
						||
        self.session.add(status)
 | 
						||
        await self.session.commit()
 | 
						||
 | 
						||
        return CreateStatusResponse(ok=True, message="Статус успешно создан")
 | 
						||
 | 
						||
    async def update_status(self, request: UpdateStatusRequest) -> UpdateStatusResponse:
 | 
						||
        status = await self._get_status_by_id(request.status.id)
 | 
						||
        if not status:
 | 
						||
            return UpdateStatusResponse(ok=False, message=f"Статус с ID {request.status.id} не найден")
 | 
						||
 | 
						||
        status.name = request.status.name
 | 
						||
        await self.session.commit()
 | 
						||
 | 
						||
        return UpdateStatusResponse(ok=True, message="Статус успешно изменен")
 | 
						||
 | 
						||
    async def update_status_order(self, request: UpdateStatusOrderRequest) -> UpdateStatusOrderResponse:
 | 
						||
        statuses = await self._get_statuses_for_board(request.board_id)
 | 
						||
        status_idx = 0
 | 
						||
        while status_idx < len(statuses) and statuses[status_idx].id != request.status_id:
 | 
						||
            status_idx += 1
 | 
						||
        if status_idx == len(statuses):
 | 
						||
            return UpdateStatusOrderResponse(ok=False, message=f"Статус с ID {request.status_id} не найден")
 | 
						||
 | 
						||
        status = statuses.pop(status_idx)
 | 
						||
        statuses.insert(request.new_ordinal_number - 1, status)
 | 
						||
        new_ordinal_number = 1
 | 
						||
        for status in statuses:
 | 
						||
            status.ordinal_number = new_ordinal_number
 | 
						||
            status.is_finishing = False
 | 
						||
            new_ordinal_number += 1
 | 
						||
 | 
						||
        statuses[-1].is_finishing = True
 | 
						||
 | 
						||
        await self.session.commit()
 | 
						||
        return UpdateStatusOrderResponse(ok=True, message="Порядок статусов изменен")
 | 
						||
 | 
						||
    async def _count_deals_in_progress(self, status_id: int) -> int:
 | 
						||
        stmt = (
 | 
						||
            select(func.count(Card.id))
 | 
						||
            .where(
 | 
						||
                and_(
 | 
						||
                    Card.current_status_id == status_id,
 | 
						||
                    Card.is_deleted == False,
 | 
						||
                    Card.is_completed == False,
 | 
						||
                )
 | 
						||
            )
 | 
						||
        )
 | 
						||
        return (await self.session.scalars(stmt)).first()
 | 
						||
 | 
						||
    async def _count_deals(self, status_id: int) -> int:
 | 
						||
        stmt = (
 | 
						||
            select(func.count(Card.id))
 | 
						||
            .where(Card.current_status_id == status_id)
 | 
						||
        )
 | 
						||
        return (await self.session.scalars(stmt)).first()
 | 
						||
 | 
						||
    async def _set_finishing_flag_to_prev_status(self, status: CardStatus):
 | 
						||
        statuses = await self._get_statuses_for_board(status.board_id)
 | 
						||
        if len(statuses) < 2:
 | 
						||
            return
 | 
						||
        statuses[-2].is_finishing = True
 | 
						||
        statuses[-1].is_finishing = False
 | 
						||
 | 
						||
    async def _status_has_history(self, status_id: int) -> bool:
 | 
						||
        stmt = (
 | 
						||
            select(exists(CardStatusHistory))
 | 
						||
            .where(
 | 
						||
                or_(
 | 
						||
                    CardStatusHistory.to_status_id == status_id,
 | 
						||
                    CardStatusHistory.from_status_id == status_id,
 | 
						||
                )
 | 
						||
            )
 | 
						||
        )
 | 
						||
        return (await self.session.scalars(stmt)).first()
 | 
						||
 | 
						||
    async def delete_status(self, status_id: int) -> DeleteStatusResponse:
 | 
						||
        status = await self._get_status_by_id(status_id)
 | 
						||
        if not status:
 | 
						||
            return DeleteStatusResponse(ok=False, message=f"Статус с ID {status_id} не найден")
 | 
						||
 | 
						||
        count_deals_in_progress = await self._count_deals_in_progress(status_id)
 | 
						||
        if count_deals_in_progress != 0:
 | 
						||
            return DeleteStatusResponse(ok=False, message="Нельзя удалить статус с активными сделками")
 | 
						||
 | 
						||
        if status.is_finishing:
 | 
						||
            await self._set_finishing_flag_to_prev_status(status)
 | 
						||
 | 
						||
        count_deals = await self._count_deals(status_id)
 | 
						||
        exist_in_history = await self._status_has_history(status_id)
 | 
						||
        if count_deals == 0 and not exist_in_history:
 | 
						||
            await self.session.delete(status)
 | 
						||
        else:
 | 
						||
            status.is_deleted = True
 | 
						||
        await self.session.commit()
 | 
						||
 | 
						||
        return DeleteStatusResponse(ok=True, message="Статус успешно удален")
 |