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='Услуги успешно добавлены')