159 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			159 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import models.secondary
 | 
						|
from typing import Union
 | 
						|
 | 
						|
from fastapi import HTTPException
 | 
						|
from sqlalchemy import select, func
 | 
						|
from sqlalchemy.orm import joinedload, selectinload
 | 
						|
 | 
						|
from models import User, Service
 | 
						|
from models.deal import *
 | 
						|
from schemas.client import ClientDetailsSchema
 | 
						|
from schemas.deal import *
 | 
						|
from services.base import BaseService
 | 
						|
from services.client import ClientService
 | 
						|
 | 
						|
 | 
						|
class DealService(BaseService):
 | 
						|
 | 
						|
    async def _get_deal_by_id(self, deal_id) -> Union[Deal, None]:
 | 
						|
        return await self.session.get(Deal, deal_id)
 | 
						|
 | 
						|
    async def change_status(self, deal: Deal,
 | 
						|
                            status: DealStatus,
 | 
						|
                            user: User,
 | 
						|
                            deadline: datetime.datetime = None) -> DealStatusHistory:
 | 
						|
        deadline = deadline
 | 
						|
        status_change = DealStatusHistory(
 | 
						|
            deal_id=deal.id,
 | 
						|
            user_id=user.id,
 | 
						|
            changed_at=datetime.datetime.now(),
 | 
						|
            from_status=deal.current_status,
 | 
						|
            to_status=status,
 | 
						|
            next_status_deadline=deadline
 | 
						|
        )
 | 
						|
        self.session.add(status_change)
 | 
						|
        await self.session.flush()
 | 
						|
        return status_change
 | 
						|
 | 
						|
    async def create(self, request: DealCreateRequest, user: User) -> DealCreateResponse:
 | 
						|
        deal = Deal(
 | 
						|
            name=request.name,
 | 
						|
            created_at=datetime.datetime.now(),
 | 
						|
            current_status=DealStatus.CREATED
 | 
						|
        )
 | 
						|
        self.session.add(deal)
 | 
						|
        await self.session.flush()
 | 
						|
 | 
						|
        # Append status history
 | 
						|
        await self.change_status(deal, DealStatus.AWAITING_ACCEPTANCE, user)
 | 
						|
 | 
						|
        await self.session.commit()
 | 
						|
        return DealCreateResponse(ok=True)
 | 
						|
 | 
						|
    async def quick_create(self, request: DealQuickCreateRequest, user: User) -> DealQuickCreateResponse:
 | 
						|
        client_service = ClientService(self.session)
 | 
						|
        client = await client_service.get_by_name(request.client_name)
 | 
						|
        if not client:
 | 
						|
            client = await client_service.create_client_raw(
 | 
						|
                user,
 | 
						|
                request.client_name,
 | 
						|
                ClientDetailsSchema(address=request.client_address))
 | 
						|
        await client_service.update_details(user, client, ClientDetailsSchema(address=request.client_address))
 | 
						|
        deal = Deal(
 | 
						|
            name=request.name,
 | 
						|
            created_at=datetime.datetime.now(),
 | 
						|
            client_id=client.id,
 | 
						|
            current_status=DealStatus.CREATED
 | 
						|
        )
 | 
						|
        self.session.add(deal)
 | 
						|
        await self.session.flush()
 | 
						|
        await self.change_status(deal, DealStatus.AWAITING_ACCEPTANCE, user, deadline=request.acceptance_date)
 | 
						|
        await self.session.commit()
 | 
						|
        return DealQuickCreateResponse(deal_id=deal.id)
 | 
						|
 | 
						|
    async def change_status_manual(self, request: DealChangeStatusRequest, user: User) -> DealChangeStatusResponse:
 | 
						|
        # Changing current status
 | 
						|
        deal = await self._get_deal_by_id(request.deal_id)
 | 
						|
        if not deal:
 | 
						|
            return DealChangeStatusResponse(ok=False)
 | 
						|
        await self.change_status(deal, DealStatus(request.new_status), user)
 | 
						|
        await self.session.commit()
 | 
						|
        return DealChangeStatusResponse(ok=True)
 | 
						|
 | 
						|
    async def get_summary(self) -> DealSummaryResponse:
 | 
						|
        service_subquery = (
 | 
						|
            select(
 | 
						|
                models.secondary.DealService.deal_id,
 | 
						|
                func.sum(models.secondary.DealService.quantity * Service.price).label('total_price')
 | 
						|
            )
 | 
						|
            .join(Service)
 | 
						|
            .group_by(models.secondary.DealService.deal_id)
 | 
						|
            .subquery()
 | 
						|
        )
 | 
						|
        q = (select(
 | 
						|
            Deal,
 | 
						|
            func.coalesce(service_subquery.c.total_price, 0)
 | 
						|
        )
 | 
						|
             .options(selectinload(Deal.status_history),
 | 
						|
                      joinedload(Deal.client))
 | 
						|
             .outerjoin(service_subquery, Deal.id == service_subquery.c.deal_id)
 | 
						|
             .where(Deal.is_deleted == False,
 | 
						|
                    Deal.is_completed == False))
 | 
						|
        deals_query = await self.session.execute(q)
 | 
						|
        summaries = []
 | 
						|
        for deal, total_price in deals_query.all():
 | 
						|
            deal: Deal
 | 
						|
            last_status: DealStatusHistory = max(deal.status_history, key=lambda status: status.changed_at)
 | 
						|
            summaries.append(
 | 
						|
                DealSummary(
 | 
						|
                    id=deal.id,
 | 
						|
                    client_name=deal.client.name,
 | 
						|
                    name=deal.name,
 | 
						|
                    changed_at=last_status.changed_at,
 | 
						|
                    status=last_status.to_status,
 | 
						|
                    total_price=total_price
 | 
						|
                )
 | 
						|
            )
 | 
						|
        return DealSummaryResponse(summaries=summaries)
 | 
						|
 | 
						|
    async def add_services(self, request: DealAddServicesRequest):
 | 
						|
        # TODO refactor
 | 
						|
        deal: Deal = await self.session.scalar(
 | 
						|
            select(Deal)
 | 
						|
            .options(selectinload(Deal.services))
 | 
						|
            .where(Deal.id == request.deal_id)
 | 
						|
        )
 | 
						|
        if not deal:
 | 
						|
            raise HTTPException(status_code=404, detail="Deal is not found")
 | 
						|
 | 
						|
        services_ids = [service.id for service in request.services]
 | 
						|
        existing_service_ids = {service.service_id for service in deal.services}
 | 
						|
        request_services_dict = {service.id: service.quantity for service in request.services}
 | 
						|
 | 
						|
        services_query = await self.session.scalars(select(Service).where(Service.id.in_(services_ids)))
 | 
						|
        services = services_query.all()
 | 
						|
        if len(services) != len(services_ids):
 | 
						|
            raise HTTPException(status_code=404, detail="Some of services is not found")
 | 
						|
 | 
						|
        # Adding quantity
 | 
						|
        for deal_service in deal.services:
 | 
						|
            deal_service: models.secondary.DealService
 | 
						|
            if deal_service.service_id not in services_ids:
 | 
						|
                continue
 | 
						|
            deal_service.quantity += request_services_dict[deal_service.service_id]
 | 
						|
 | 
						|
        # Adding new services
 | 
						|
        for service in services:
 | 
						|
            if service.id in existing_service_ids:
 | 
						|
                continue
 | 
						|
            quantity = request_services_dict[service.id]
 | 
						|
            deal.services.append(
 | 
						|
                models.secondary.DealService(
 | 
						|
                    service=service,
 | 
						|
                    deal=deal,
 | 
						|
                    quantity=quantity
 | 
						|
                )
 | 
						|
            )
 | 
						|
        await self.session.commit()
 | 
						|
        return DealAddServicesResponse(ok=True, message='Услуги успешно добавлены')
 |