1106 lines
		
	
	
		
			48 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			1106 lines
		
	
	
		
			48 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import lexorank
 | 
						||
from fastapi import HTTPException
 | 
						||
from sqlalchemy import select, func, update, delete, insert
 | 
						||
from sqlalchemy.orm import joinedload, selectinload
 | 
						||
from starlette import status
 | 
						||
 | 
						||
import models.deal
 | 
						||
import models.secondary
 | 
						||
from models import User, Service, Client, DealProductService
 | 
						||
from models.deal import *
 | 
						||
from schemas.client import ClientDetailsSchema
 | 
						||
from schemas.deal import *
 | 
						||
from services.auth import AuthService
 | 
						||
from services.base import BaseService
 | 
						||
from services.client import ClientService
 | 
						||
from services.service import ServiceService
 | 
						||
from services.shipping_warehouse import ShippingWarehouseService
 | 
						||
 | 
						||
 | 
						||
class DealService(BaseService):
 | 
						||
 | 
						||
    # region Deal
 | 
						||
 | 
						||
    @staticmethod
 | 
						||
    def grant_access(user: Union[models.User, dict], deal_id):
 | 
						||
        if type(user) is models.User:
 | 
						||
            return
 | 
						||
        user_deal_id = user['deal_id']
 | 
						||
        if int(user_deal_id) != int(deal_id):
 | 
						||
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='Invalid token')
 | 
						||
 | 
						||
    async def _get_deal_by_id(self, deal_id) -> Union[Deal, None]:
 | 
						||
        return await self.session.get(Deal, deal_id)
 | 
						||
 | 
						||
    async def _get_rank_for_deal(self, deal_status: DealStatus) -> str:
 | 
						||
        deal_query = await self.session.execute(
 | 
						||
            select(Deal).where(Deal.current_status == deal_status).order_by(Deal.lexorank.desc()).limit(1))
 | 
						||
        deal = deal_query.scalar_one_or_none()
 | 
						||
        if not deal:
 | 
						||
            prev = lexorank.middle(lexorank.Bucket.BUCEKT_0)
 | 
						||
            return str(prev.next())
 | 
						||
        return str(lexorank.parse(deal.lexorank).next())
 | 
						||
 | 
						||
    async def change_status(self, deal: Deal,
 | 
						||
                            status: DealStatus,
 | 
						||
                            user: User,
 | 
						||
                            deadline: datetime.datetime = None,
 | 
						||
                            rank=None,
 | 
						||
                            comment: str = ''):
 | 
						||
        if not deal.current_status == status:
 | 
						||
            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,
 | 
						||
                comment=comment
 | 
						||
            )
 | 
						||
            self.session.add(status_change)
 | 
						||
            deal.current_status = status
 | 
						||
            if not rank:
 | 
						||
                rank = await self._get_rank_for_deal(status)
 | 
						||
        if rank:
 | 
						||
            deal.lexorank = rank
 | 
						||
        await self.session.flush()
 | 
						||
 | 
						||
    async def create(self, request: DealCreateRequest, user: User) -> DealCreateResponse:
 | 
						||
        rank = self._get_rank_for_deal(DealStatus.CREATED)
 | 
						||
        deal = Deal(
 | 
						||
            name=request.name,
 | 
						||
            created_at=datetime.datetime.now(),
 | 
						||
            current_status=DealStatus.CREATED,
 | 
						||
            lexorank=rank
 | 
						||
        )
 | 
						||
        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 delete(self, request: DealDeleteRequest) -> DealDeleteResponse:
 | 
						||
        deal = await self._get_deal_by_id(request.deal_id)
 | 
						||
        if not deal:
 | 
						||
            return DealDeleteResponse(ok=False, message="Сделка не найдена")
 | 
						||
        deal.is_deleted = True
 | 
						||
        await self.session.commit()
 | 
						||
        return DealDeleteResponse(ok=True, message="Сделка успешно удалена")
 | 
						||
 | 
						||
    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()
 | 
						||
            )
 | 
						||
        shipping_warehouse_service = ShippingWarehouseService(self.session)
 | 
						||
        shipping_warehouse = await shipping_warehouse_service.get_by_name(name=request.shipping_warehouse)
 | 
						||
        if not shipping_warehouse:
 | 
						||
            shipping_warehouse = await shipping_warehouse_service.create_by_name(name=request.shipping_warehouse)
 | 
						||
 | 
						||
        rank = await self._get_rank_for_deal(DealStatus.CREATED)
 | 
						||
        deal = Deal(
 | 
						||
            name=request.name,
 | 
						||
            created_at=datetime.datetime.now(),
 | 
						||
            client_id=client.id,
 | 
						||
            current_status=DealStatus.CREATED,
 | 
						||
            lexorank=rank,
 | 
						||
            shipping_warehouse_id=shipping_warehouse.id,
 | 
						||
            base_marketplace_key=request.base_marketplace.key
 | 
						||
        )
 | 
						||
        self.session.add(deal)
 | 
						||
        await self.session.flush()
 | 
						||
        await self.change_status(deal,
 | 
						||
                                 DealStatus.AWAITING_ACCEPTANCE,
 | 
						||
                                 user,
 | 
						||
                                 deadline=request.acceptance_date,
 | 
						||
                                 comment=request.comment)
 | 
						||
        # add category if specified
 | 
						||
        if request.category:
 | 
						||
            deal_category = DealPriceCategory(
 | 
						||
                deal_id=deal.id,
 | 
						||
                category_id=request.category.id
 | 
						||
            )
 | 
						||
            self.session.add(deal_category)
 | 
						||
 | 
						||
        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)
 | 
						||
 | 
						||
    def _get_price_subquery(self):
 | 
						||
        deal_services_subquery = (
 | 
						||
            select(
 | 
						||
                models.secondary.DealService.deal_id,
 | 
						||
                func.sum(models.secondary.DealService.quantity * models.secondary.DealService.price).label(
 | 
						||
                    'total_price')
 | 
						||
            )
 | 
						||
            .join(Service)
 | 
						||
            .group_by(models.secondary.DealService.deal_id)
 | 
						||
        )
 | 
						||
        product_services_subquery = select(
 | 
						||
            select(
 | 
						||
                models.secondary.DealProductService.deal_id,
 | 
						||
                func.sum(models.DealProduct.quantity * models.secondary.DealProductService.price).label('total_price')
 | 
						||
            )
 | 
						||
            .join(models.secondary.DealProduct)
 | 
						||
            .group_by(models.secondary.DealProductService.deal_id)
 | 
						||
            .subquery()
 | 
						||
        )
 | 
						||
        union_subqueries = deal_services_subquery.union(product_services_subquery).subquery()
 | 
						||
        final_subquery = (
 | 
						||
            select(
 | 
						||
                union_subqueries.c.deal_id,
 | 
						||
                func.sum(union_subqueries.c.total_price).label('total_price')
 | 
						||
            )
 | 
						||
            .group_by(union_subqueries.c.deal_id)
 | 
						||
            .subquery()
 | 
						||
        )
 | 
						||
        return final_subquery
 | 
						||
 | 
						||
    def _get_products_quantity_subquery(self):
 | 
						||
        deal_products_subquery = (
 | 
						||
            select(
 | 
						||
                models.secondary.DealProduct.deal_id,
 | 
						||
                func.sum(models.secondary.DealProduct.quantity).label('total_quantity')
 | 
						||
            )
 | 
						||
            .group_by(models.secondary.DealProduct.deal_id)
 | 
						||
            .subquery()
 | 
						||
        )
 | 
						||
        return deal_products_subquery
 | 
						||
 | 
						||
    async def get_summary(self, full: bool = False) -> DealSummaryResponse:
 | 
						||
        price_subquery = self._get_price_subquery()
 | 
						||
        products_quantity_subquery = self._get_products_quantity_subquery()
 | 
						||
        q = (
 | 
						||
            select(
 | 
						||
                Deal,
 | 
						||
                func.coalesce(price_subquery.c.total_price, 0),
 | 
						||
                func.row_number().over(
 | 
						||
                    partition_by=Deal.current_status,
 | 
						||
                    order_by=Deal.lexorank
 | 
						||
                ).label('rank'),
 | 
						||
                func.coalesce(products_quantity_subquery.c.total_quantity, 0)
 | 
						||
            )
 | 
						||
            .options(
 | 
						||
                selectinload(Deal.status_history),
 | 
						||
                joinedload(Deal.client),
 | 
						||
                joinedload(Deal.shipping_warehouse),
 | 
						||
                joinedload(Deal.bill_request)
 | 
						||
            )
 | 
						||
            .outerjoin(
 | 
						||
                price_subquery, Deal.id == price_subquery.c.deal_id,
 | 
						||
            )
 | 
						||
            .outerjoin(
 | 
						||
                products_quantity_subquery, Deal.id == products_quantity_subquery.c.deal_id
 | 
						||
            )
 | 
						||
            .where(
 | 
						||
                Deal.is_deleted == False,
 | 
						||
            )
 | 
						||
        )
 | 
						||
        if not full:
 | 
						||
            q = q.where(
 | 
						||
                Deal.is_completed == False,
 | 
						||
                # Deal.current_status != DealStatus.COMPLETED
 | 
						||
            )
 | 
						||
        else:
 | 
						||
            q = q.order_by(Deal.created_at.desc())
 | 
						||
        deals_query = await self.session.execute(q)
 | 
						||
        summaries = []
 | 
						||
        for deal, total_price, rank, products_count in deals_query.all():
 | 
						||
            deal: Deal
 | 
						||
            last_status: DealStatusHistory = max(deal.status_history, key=lambda status: status.changed_at)
 | 
						||
            deadline = last_status.next_status_deadline
 | 
						||
            base_marketplace = None
 | 
						||
            if deal.base_marketplace:
 | 
						||
                base_marketplace = BaseMarketplaceSchema.model_validate(deal.base_marketplace)
 | 
						||
            shipment_warehouse_name = deal.shipping_warehouse.name if deal.shipping_warehouse else None
 | 
						||
            summaries.append(
 | 
						||
                DealSummary(
 | 
						||
                    id=deal.id,
 | 
						||
                    client_name=deal.client.name,
 | 
						||
                    name=deal.name,
 | 
						||
                    changed_at=last_status.changed_at,
 | 
						||
                    deadline=deadline,
 | 
						||
                    status=last_status.to_status,
 | 
						||
                    total_price=total_price,
 | 
						||
                    rank=rank,
 | 
						||
                    base_marketplace=base_marketplace,
 | 
						||
                    created_at=deal.created_at,
 | 
						||
                    shipment_warehouse_id=deal.shipping_warehouse_id,
 | 
						||
                    shipment_warehouse_name=shipment_warehouse_name,
 | 
						||
                    total_products=products_count,
 | 
						||
                    delivery_date=deal.delivery_date,
 | 
						||
                    receiving_slot_date=deal.receiving_slot_date,
 | 
						||
                    bill_request=deal.bill_request,
 | 
						||
                    # group=deal.group
 | 
						||
                )
 | 
						||
            )
 | 
						||
        return DealSummaryResponse(summaries=summaries)
 | 
						||
 | 
						||
    async def get_all(self) -> DealGetAllResponse:
 | 
						||
        deals_query = (
 | 
						||
            await self.session.scalars(
 | 
						||
                select(
 | 
						||
                    Deal
 | 
						||
                )
 | 
						||
                .options(
 | 
						||
                    joinedload(Deal.shipping_warehouse),
 | 
						||
                    joinedload(Deal.client)
 | 
						||
                    .joinedload(Client.details),
 | 
						||
                    selectinload(Deal.services)
 | 
						||
                    .options(
 | 
						||
                        joinedload(models.secondary.DealService.service).joinedload(Service.category),
 | 
						||
                        selectinload(models.secondary.DealService.employees)
 | 
						||
                    ),
 | 
						||
                    selectinload(Deal.products)
 | 
						||
                    .joinedload(models.secondary.DealProduct.product)
 | 
						||
                    .joinedload(models.Product.client),
 | 
						||
                    selectinload(Deal.products)
 | 
						||
                    .joinedload(models.secondary.DealProduct.product)
 | 
						||
                    .joinedload(models.Product.barcodes),
 | 
						||
                    selectinload(Deal.products)
 | 
						||
                    .joinedload(models.secondary.DealProduct.services)
 | 
						||
                    .options(
 | 
						||
                        joinedload(models.secondary.DealProductService.service),
 | 
						||
                        selectinload(models.secondary.DealProductService.employees)
 | 
						||
                    ),
 | 
						||
                    selectinload(Deal.status_history)
 | 
						||
                    .joinedload(DealStatusHistory.user),
 | 
						||
                    selectinload(Deal.status_history)
 | 
						||
                    .noload(DealStatusHistory.deal),
 | 
						||
 | 
						||
                )
 | 
						||
            )
 | 
						||
        )
 | 
						||
        deals = deals_query.all()
 | 
						||
        result = []
 | 
						||
        for deal in deals:
 | 
						||
            result.append(DealSchema.model_validate(deal))
 | 
						||
        return DealGetAllResponse(deals=result)
 | 
						||
 | 
						||
    async def get_by_id(self, user: Union[models.User, dict], deal_id: int, return_raw=False) -> Union[
 | 
						||
        DealSchema, models.Deal]:
 | 
						||
        self.grant_access(user, deal_id)
 | 
						||
 | 
						||
        deal = await self.session.scalar(
 | 
						||
            select(Deal)
 | 
						||
            .options(
 | 
						||
                joinedload(Deal.shipping_warehouse),
 | 
						||
                joinedload(Deal.client)
 | 
						||
                .joinedload(Client.details),
 | 
						||
                selectinload(Deal.services)
 | 
						||
                .options(
 | 
						||
                    joinedload(models.secondary.DealService.service).joinedload(Service.category),
 | 
						||
                    selectinload(models.secondary.DealService.employees)
 | 
						||
                ),
 | 
						||
                selectinload(Deal.products)
 | 
						||
                .joinedload(models.secondary.DealProduct.product)
 | 
						||
                .joinedload(models.Product.client),
 | 
						||
                selectinload(Deal.products)
 | 
						||
                .joinedload(models.secondary.DealProduct.product)
 | 
						||
                .joinedload(models.Product.barcodes),
 | 
						||
                selectinload(Deal.products)
 | 
						||
                .joinedload(models.secondary.DealProduct.services)
 | 
						||
                .options(
 | 
						||
                    joinedload(models.secondary.DealProductService.service),
 | 
						||
                    selectinload(models.secondary.DealProductService.employees)
 | 
						||
                ),
 | 
						||
                selectinload(Deal.status_history)
 | 
						||
                .joinedload(DealStatusHistory.user),
 | 
						||
                selectinload(Deal.status_history)
 | 
						||
                .noload(DealStatusHistory.deal),
 | 
						||
 | 
						||
            )
 | 
						||
            .where(Deal.id == deal_id)
 | 
						||
        )
 | 
						||
        if return_raw:
 | 
						||
            return deal
 | 
						||
        if not deal:
 | 
						||
            raise HTTPException(status_code=404, detail="Сделка не найдена")
 | 
						||
        return DealSchema.model_validate(deal)
 | 
						||
 | 
						||
    async def update_general_info(self, request: DealUpdateGeneralInfoRequest) -> DealUpdateGeneralInfoResponse:
 | 
						||
        try:
 | 
						||
            deal: Deal = await self.session.scalar(select(Deal).where(Deal.id == request.deal_id))
 | 
						||
            if not deal:
 | 
						||
                raise HTTPException(status_code=404, detail="Сделка не найдена")
 | 
						||
            deal.name = request.data.name
 | 
						||
            deal.comment = request.data.comment
 | 
						||
            deal.is_deleted = request.data.is_deleted
 | 
						||
            deal.is_completed = request.data.is_completed
 | 
						||
            deal.delivery_date = request.data.delivery_date
 | 
						||
            deal.receiving_slot_date = request.data.receiving_slot_date
 | 
						||
 | 
						||
            # Updating shipping warehouse
 | 
						||
            shipping_warehouse_service = ShippingWarehouseService(self.session)
 | 
						||
            shipping_warehouse = await shipping_warehouse_service.get_by_name(request.data.shipping_warehouse)
 | 
						||
            if not shipping_warehouse and request.data.shipping_warehouse:
 | 
						||
                shipping_warehouse = await shipping_warehouse_service.create_by_name(request.data.shipping_warehouse)
 | 
						||
 | 
						||
            deal.shipping_warehouse = shipping_warehouse
 | 
						||
            await self.session.commit()
 | 
						||
            return DealUpdateGeneralInfoResponse(ok=True, message='Данные о сделке успешно обновлены')
 | 
						||
        except Exception as e:
 | 
						||
            await self.session.rollback()
 | 
						||
            return DealUpdateGeneralInfoResponse(ok=False, message=str(e))
 | 
						||
 | 
						||
    async def reorder(self, request: DealSummaryReorderRequest, user: User) -> DealSummaryResponse:
 | 
						||
        deal: Deal = await self.session.scalar(select(Deal).where(Deal.id == request.deal_id))
 | 
						||
        if request.index == 1:
 | 
						||
            request.index = 0
 | 
						||
        is_first = request.index == 0
 | 
						||
        stmt = (
 | 
						||
            select(Deal)
 | 
						||
            .where(
 | 
						||
                Deal.current_status == request.status,
 | 
						||
                Deal.id != request.deal_id,
 | 
						||
                Deal.is_deleted == False,
 | 
						||
                Deal.is_completed == False
 | 
						||
            )
 | 
						||
            .order_by(Deal.lexorank)
 | 
						||
            .offset(max([request.index - 2, 0]))
 | 
						||
            .limit(2 if not is_first else 1)
 | 
						||
 | 
						||
        )
 | 
						||
        query = await self.session.execute(stmt)
 | 
						||
        boundaries = query.scalars().all()
 | 
						||
        top_boundary: Union[Deal, None] = boundaries[0] if not is_first else None
 | 
						||
        bottom_boundary: Union[Deal, None] = boundaries[1] if len(boundaries) == 2 else None
 | 
						||
        # working when between two elements
 | 
						||
        if top_boundary and bottom_boundary:
 | 
						||
            top_lexorank = lexorank.parse(top_boundary.lexorank)
 | 
						||
            bottom_lexorank = lexorank.parse(bottom_boundary.lexorank)
 | 
						||
            new_rank = lexorank.between(top_lexorank, bottom_lexorank)
 | 
						||
 | 
						||
        # working when at the bottom
 | 
						||
        elif top_boundary and not bottom_boundary:
 | 
						||
            new_rank = lexorank.parse(top_boundary.lexorank).next()
 | 
						||
        # working when at the top
 | 
						||
        elif bottom_boundary and not top_boundary:
 | 
						||
            new_rank = lexorank.parse(bottom_boundary.lexorank).prev()
 | 
						||
        elif not top_boundary and not bottom_boundary and len(boundaries) > 0:
 | 
						||
            new_rank = lexorank.parse(boundaries[0].lexorank).prev()
 | 
						||
        else:
 | 
						||
            new_rank = lexorank.middle(lexorank.Bucket.BUCEKT_0)
 | 
						||
 | 
						||
        await self.change_status(deal, request.status, user,
 | 
						||
                                 deadline=request.deadline,
 | 
						||
                                 comment=request.comment,
 | 
						||
                                 rank=str(new_rank))
 | 
						||
        await self.session.commit()
 | 
						||
        return await self.get_summary()
 | 
						||
 | 
						||
    async def add_kit_to_deal(self, request: DealAddKitRequest) -> DealAddKitResponse:
 | 
						||
        try:
 | 
						||
            deal = await self._get_deal_by_id(request.deal_id)
 | 
						||
            if not deal:
 | 
						||
                return DealAddKitResponse(ok=False, message="Указанная сделка не найдена")
 | 
						||
            kit = await ServiceService(self.session).get_kit_by_id(request.kit_id)
 | 
						||
            if not kit:
 | 
						||
                return DealAddKitResponse(ok=False, message="Указанный набор услуг не найден")
 | 
						||
            services: list[models.Service] = kit.services
 | 
						||
            insert_data = []
 | 
						||
            for service in services:
 | 
						||
                price = self.get_service_price(service, 1)
 | 
						||
                insert_data.append({
 | 
						||
                    'deal_id': deal.id,
 | 
						||
                    'service_id': service.id,
 | 
						||
                    'quantity': 1,
 | 
						||
                    'price': price
 | 
						||
                })
 | 
						||
            if not insert_data:
 | 
						||
                return DealAddKitResponse(ok=True, message="Набор услуг успешно добавлен к сделке")
 | 
						||
            # Deleting previous services
 | 
						||
            delete_stmt = (
 | 
						||
                delete(
 | 
						||
                    models.DealService
 | 
						||
                )
 | 
						||
                .where(
 | 
						||
                    models.DealService.deal_id == request.deal_id
 | 
						||
                )
 | 
						||
            )
 | 
						||
            await self.session.execute(delete_stmt)
 | 
						||
            await self.session.flush()
 | 
						||
 | 
						||
            await self.session.execute(
 | 
						||
                insert(models.DealService),
 | 
						||
                insert_data
 | 
						||
            )
 | 
						||
            await self.session.flush()
 | 
						||
            await self.session.commit()
 | 
						||
            return DealAddKitResponse(ok=True, message="Набор услуг успешно добавлен к сделке")
 | 
						||
        except Exception as e:
 | 
						||
            return DealAddKitResponse(ok=False, message=str(e))
 | 
						||
 | 
						||
    def create_guest_url(self, user: models.User, request: DealCreateGuestUrlRequest) -> DealCreateGuestUrlResponse:
 | 
						||
        # if not user.is_admin:
 | 
						||
        #     return DealCreateGuestUrlResponse(ok=False, message='Создать ссылку может только администратор', url="")
 | 
						||
        access_token = AuthService(self.session).create_deal_guest_token(request.deal_id)
 | 
						||
        url = f"deals/{request.deal_id}?accessToken={access_token}"
 | 
						||
        return DealCreateGuestUrlResponse(ok=True, message='Ссылка успешно создана!', url=url)
 | 
						||
 | 
						||
    async def _create_deal_services_for_prefilling(self, old_deal: models.Deal, new_deal: models.Deal):
 | 
						||
        for service in old_deal.services:
 | 
						||
            deal_service = models.secondary.DealService(
 | 
						||
                service_id=service.service_id,
 | 
						||
                deal_id=new_deal.id,
 | 
						||
                quantity=service.quantity,
 | 
						||
                price=service.price,
 | 
						||
            )
 | 
						||
            self.session.add(deal_service)
 | 
						||
 | 
						||
    async def _create_deal_products_for_prefilling(self, old_deal: models.Deal, new_deal: models.Deal):
 | 
						||
        for old_deal_product in old_deal.products:
 | 
						||
            deal_product = models.secondary.DealProduct(
 | 
						||
                deal_id=new_deal.id,
 | 
						||
                product_id=old_deal_product.product.id,
 | 
						||
                quantity=old_deal_product.quantity
 | 
						||
            )
 | 
						||
            self.session.add(deal_product)
 | 
						||
            await self.session.flush()
 | 
						||
            for old_service in old_deal_product.services:
 | 
						||
                deal_product_service = models.secondary.DealProductService(
 | 
						||
                    deal_id=new_deal.id,
 | 
						||
                    product_id=old_deal_product.product.id,
 | 
						||
                    service_id=old_service.service.id,
 | 
						||
                    price=old_service.price
 | 
						||
                )
 | 
						||
                self.session.add(deal_product_service)
 | 
						||
 | 
						||
    async def prefill_deal(self, user, request: DealPrefillRequest) -> DealPrefillResponse:
 | 
						||
        old_deal: models.Deal = await self.get_by_id(user, request.old_deal_id, return_raw=True)
 | 
						||
        new_deal: models.Deal = await self.get_by_id(user, request.new_deal_id, return_raw=True)
 | 
						||
 | 
						||
        await self._create_deal_services_for_prefilling(old_deal, new_deal)
 | 
						||
        await self._create_deal_products_for_prefilling(old_deal, new_deal)
 | 
						||
        await self.session.commit()
 | 
						||
 | 
						||
        return DealPrefillResponse(ok=True, message="Сделка успешно предзаполнена")
 | 
						||
 | 
						||
    # endregion
 | 
						||
 | 
						||
    # region Deal services
 | 
						||
    async def add_services(self, request: DealAddServicesRequest):
 | 
						||
        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_id=service.id,
 | 
						||
                    deal_id=deal.id,
 | 
						||
                    quantity=quantity
 | 
						||
                )
 | 
						||
            )
 | 
						||
        await self.session.commit()
 | 
						||
        return DealAddServicesResponse(ok=True, message='Услуги успешно добавлены')
 | 
						||
 | 
						||
    async def update_service_quantity(
 | 
						||
            self,
 | 
						||
            user: Union[models.User, dict],
 | 
						||
            request: DealUpdateServiceQuantityRequest
 | 
						||
    ) -> DealUpdateServiceQuantityResponse:
 | 
						||
        try:
 | 
						||
            self.grant_access(user, request.deal_id)
 | 
						||
            deal_service = await self.session.scalar(
 | 
						||
                select(models.secondary.DealService)
 | 
						||
                .where(models.secondary.DealService.deal_id == request.deal_id,
 | 
						||
                       models.secondary.DealService.service_id == request.service_id)
 | 
						||
            )
 | 
						||
            if not deal_service:
 | 
						||
                raise HTTPException(status_code=404, detail="Сделка не найдена")
 | 
						||
            deal_service.quantity = request.quantity
 | 
						||
            await self.session.commit()
 | 
						||
            return DealUpdateServiceQuantityResponse(ok=True, message='Количество успешно обновлено')
 | 
						||
        except Exception as e:
 | 
						||
            await self.session.rollback()
 | 
						||
            return DealUpdateServiceQuantityResponse(ok=False, message=str(e))
 | 
						||
 | 
						||
    async def add_service(self,
 | 
						||
                          user: Union[models.User, dict],
 | 
						||
                          request: DealAddServiceRequest
 | 
						||
                          ) -> DealAddServiceResponse:
 | 
						||
        try:
 | 
						||
            self.grant_access(user, request.deal_id)
 | 
						||
            deal = await self.session.scalar(select(Deal).where(Deal.id == request.deal_id))
 | 
						||
            if not deal:
 | 
						||
                raise HTTPException(status_code=404, detail="Сделка не найдена")
 | 
						||
            service: models.Service = await self.session.scalar(select(Service).where(Service.id == request.service_id))
 | 
						||
            if not service:
 | 
						||
                raise HTTPException(status_code=404, detail="Услуга не найдена")
 | 
						||
            # Preventing duplicates
 | 
						||
            deal_service = await self.session.scalar(
 | 
						||
                select(models.secondary.DealService)
 | 
						||
                .where(models.secondary.DealService.deal_id == request.deal_id,
 | 
						||
                       models.secondary.DealService.service_id == request.service_id)
 | 
						||
            )
 | 
						||
            if deal_service:
 | 
						||
                raise HTTPException(status_code=400, detail="Услуга уже добавлена")
 | 
						||
            deal_service = models.secondary.DealService(
 | 
						||
                deal_id=request.deal_id,
 | 
						||
                service_id=request.service_id,
 | 
						||
                quantity=request.quantity,
 | 
						||
                price=request.price
 | 
						||
            )
 | 
						||
            self.session.add(deal_service)
 | 
						||
            await self.session.commit()
 | 
						||
            return DealAddServiceResponse(ok=True, message='Услуга успешно добавлена')
 | 
						||
        except Exception as e:
 | 
						||
            await self.session.rollback()
 | 
						||
            return DealAddServiceResponse(ok=False, message=str(e))
 | 
						||
 | 
						||
    async def delete_service(
 | 
						||
            self,
 | 
						||
            user: Union[models.User, dict],
 | 
						||
            request: DealDeleteServiceRequest
 | 
						||
    ) -> DealDeleteServiceResponse:
 | 
						||
        try:
 | 
						||
            self.grant_access(user, request.deal_id)
 | 
						||
            deal_service = await self.session.scalar(
 | 
						||
                select(models.secondary.DealService)
 | 
						||
                .where(models.secondary.DealService.deal_id == request.deal_id,
 | 
						||
                       models.secondary.DealService.service_id == request.service_id)
 | 
						||
            )
 | 
						||
            if not deal_service:
 | 
						||
                raise HTTPException(status_code=404, detail="Сделка не найдена")
 | 
						||
            await self.session.delete(deal_service)
 | 
						||
            await self.session.commit()
 | 
						||
            return DealDeleteServiceResponse(ok=True, message='Услуга успешно удалена')
 | 
						||
        except Exception as e:
 | 
						||
            await self.session.rollback()
 | 
						||
            return DealDeleteServiceResponse(ok=False, message=str(e))
 | 
						||
 | 
						||
    async def delete_services(
 | 
						||
            self,
 | 
						||
            user: Union[models.User, dict],
 | 
						||
            request: DealDeleteServicesRequest
 | 
						||
    ) -> DealDeleteServicesResponse:
 | 
						||
        try:
 | 
						||
            self.grant_access(user, request)
 | 
						||
            deal_services = await self.session.scalars(
 | 
						||
                select(models.secondary.DealService)
 | 
						||
                .where(models.secondary.DealService.deal_id == request.deal_id,
 | 
						||
                       models.secondary.DealService.service_id.in_(request.service_ids))
 | 
						||
            )
 | 
						||
            for deal_service in deal_services:
 | 
						||
                await self.session.delete(deal_service)
 | 
						||
            await self.session.commit()
 | 
						||
            return DealDeleteServicesResponse(ok=True, message='Услуги успешно удалены')
 | 
						||
        except Exception as e:
 | 
						||
            await self.session.rollback()
 | 
						||
            return DealDeleteServicesResponse(ok=False, message=str(e))
 | 
						||
 | 
						||
    async def update_service(
 | 
						||
            self,
 | 
						||
            user: Union[models.User, dict],
 | 
						||
            request: DealUpdateServiceRequest
 | 
						||
    ) -> DealUpdateServiceResponse:
 | 
						||
        try:
 | 
						||
            self.grant_access(user, request.deal_id)
 | 
						||
            deal_service = await self.session.scalar(
 | 
						||
                select(models.secondary.DealService)
 | 
						||
                .where(models.secondary.DealService.deal_id == request.deal_id,
 | 
						||
                       models.secondary.DealService.service_id == request.service.service.id)
 | 
						||
            )
 | 
						||
            if not deal_service:
 | 
						||
                raise HTTPException(status_code=404, detail="Сделка не найдена")
 | 
						||
            service_dict = request.service.dict()
 | 
						||
            del service_dict['service']
 | 
						||
            del service_dict['employees']
 | 
						||
 | 
						||
            service_dict['service_id'] = request.service.service.id
 | 
						||
 | 
						||
            await self.session.execute(
 | 
						||
                update(models.secondary.DealService)
 | 
						||
                .where(models.secondary.DealService.deal_id == request.deal_id,
 | 
						||
                       models.secondary.DealService.service_id == request.service.service.id)
 | 
						||
                .values(**service_dict)
 | 
						||
            )
 | 
						||
 | 
						||
            # Updating deleting previous employees
 | 
						||
            delete_stmt = (
 | 
						||
                delete(
 | 
						||
                    models.deal_service_employees
 | 
						||
                )
 | 
						||
                .where(
 | 
						||
                    models.deal_service_employees.c.deal_id == request.deal_id,
 | 
						||
                    models.deal_service_employees.c.service_id == request.service.service.id,
 | 
						||
                )
 | 
						||
            )
 | 
						||
            await self.session.execute(delete_stmt)
 | 
						||
            await self.session.flush()
 | 
						||
            insert_data = []
 | 
						||
            for employee in request.service.employees:
 | 
						||
                insert_data.append({
 | 
						||
                    'deal_id': request.deal_id,
 | 
						||
                    'service_id': request.service.service.id,
 | 
						||
                    'user_id': employee.id
 | 
						||
                })
 | 
						||
            if insert_data:
 | 
						||
                await self.session.execute(
 | 
						||
                    insert(models.deal_service_employees),
 | 
						||
                    insert_data
 | 
						||
                )
 | 
						||
                await self.session.flush()
 | 
						||
 | 
						||
            await self.session.commit()
 | 
						||
            return DealUpdateServiceQuantityResponse(ok=True, message='Услуга успешно обновлена')
 | 
						||
        except Exception as e:
 | 
						||
            await self.session.rollback()
 | 
						||
            return DealUpdateServiceQuantityResponse(ok=False, message=str(e))
 | 
						||
 | 
						||
    async def copy_services(
 | 
						||
            self,
 | 
						||
            user: Union[models.User, dict],
 | 
						||
            request: DealServicesCopyRequest
 | 
						||
    ) -> DealServicesCopyResponse:
 | 
						||
        try:
 | 
						||
            self.grant_access(user, request.deal_id)
 | 
						||
            source_services_stmt = (
 | 
						||
                select(
 | 
						||
                    models.DealProductService
 | 
						||
                )
 | 
						||
                .where(
 | 
						||
                    models.DealProductService.product_id == request.source_product_id,
 | 
						||
                    models.DealProductService.deal_id == request.deal_id,
 | 
						||
                )
 | 
						||
            )
 | 
						||
            deal_product_services = (
 | 
						||
                (
 | 
						||
                    await self.session.scalars(
 | 
						||
                        source_services_stmt
 | 
						||
                    )
 | 
						||
                )
 | 
						||
                .all()
 | 
						||
            )
 | 
						||
 | 
						||
            destination_deal_products_stmt = (
 | 
						||
                select(
 | 
						||
                    models.DealProduct
 | 
						||
                )
 | 
						||
                .where(
 | 
						||
                    models.DealProduct.product_id.in_(request.destination_product_ids),
 | 
						||
                    models.DealProduct.deal_id == request.deal_id
 | 
						||
                )
 | 
						||
            )
 | 
						||
            destination_deal_products = (await self.session.scalars(destination_deal_products_stmt)).all()
 | 
						||
            insert_data = []
 | 
						||
            for deal_product in destination_deal_products:
 | 
						||
                for service in deal_product_services:
 | 
						||
                    insert_data.append({
 | 
						||
                        'deal_id': request.deal_id,
 | 
						||
                        'product_id': deal_product.product_id,
 | 
						||
                        'service_id': service.service.id,
 | 
						||
                        'price': service.price,
 | 
						||
                        'is_fixed_price': service.is_fixed_price
 | 
						||
                    })
 | 
						||
            if not insert_data:
 | 
						||
                return DealServicesCopyResponse(ok=True, message='Услуги успешно перенесены')
 | 
						||
            # Deleting previous DealProductService-s
 | 
						||
            delete_stmt = (
 | 
						||
                delete(
 | 
						||
                    models.DealProductService
 | 
						||
                )
 | 
						||
                .where(
 | 
						||
                    models.DealProductService.product_id.in_(request.destination_product_ids),
 | 
						||
                    models.DealProductService.deal_id == request.deal_id
 | 
						||
                )
 | 
						||
            )
 | 
						||
            await self.session.execute(delete_stmt)
 | 
						||
            await self.session.flush()
 | 
						||
 | 
						||
            await self.session.execute(
 | 
						||
                insert(models.DealProductService),
 | 
						||
                insert_data
 | 
						||
            )
 | 
						||
            await self.session.flush()
 | 
						||
            await self.session.commit()
 | 
						||
            return DealServicesCopyResponse(ok=True, message='Услуги успешно перенесены')
 | 
						||
        except Exception as e:
 | 
						||
            return DealServicesCopyResponse(ok=False, message=str(e))
 | 
						||
 | 
						||
    @staticmethod
 | 
						||
    def get_service_price(service: models.Service, quantity: int):
 | 
						||
        price = 0
 | 
						||
        price_ranges: list[models.ServicePriceRange] = service.price_ranges
 | 
						||
        for price_range in price_ranges:
 | 
						||
            if price_range.from_quantity <= quantity <= price_range.to_quantity:
 | 
						||
                price = price_range.price
 | 
						||
                break
 | 
						||
 | 
						||
        if not price and len(price_ranges) > 0:
 | 
						||
            price = price_ranges[0].price
 | 
						||
        if not price:
 | 
						||
            price = service.price
 | 
						||
        return price
 | 
						||
 | 
						||
    # endregion
 | 
						||
 | 
						||
    # region Deal products
 | 
						||
    async def update_product_quantity(
 | 
						||
            self,
 | 
						||
            user: Union[models.User, dict],
 | 
						||
            request: DealUpdateProductQuantityRequest
 | 
						||
    ) -> DealUpdateProductQuantityResponse:
 | 
						||
        try:
 | 
						||
            self.grant_access(user, request.deal_id)
 | 
						||
            # check if there is no deal or no product with different exceptions
 | 
						||
            deal_product = await self.session.scalar(
 | 
						||
                select(models.secondary.DealProduct)
 | 
						||
                .where(models.secondary.DealProduct.deal_id == request.deal_id,
 | 
						||
                       models.secondary.DealProduct.product_id == request.product_id)
 | 
						||
            )
 | 
						||
            if not deal_product:
 | 
						||
                raise HTTPException(status_code=404, detail="Сделка или товар не найдена")
 | 
						||
            deal_product.quantity = request.quantity
 | 
						||
            await self.session.commit()
 | 
						||
            return DealUpdateProductQuantityResponse(ok=True, message='Количество успешно обновлено')
 | 
						||
        except Exception as e:
 | 
						||
            await self.session.rollback()
 | 
						||
            return DealUpdateProductQuantityResponse(ok=False, message=str(e))
 | 
						||
 | 
						||
    async def add_product(
 | 
						||
            self,
 | 
						||
            user: Union[models.User, dict],
 | 
						||
 | 
						||
            request: DealAddProductRequest
 | 
						||
    ) -> DealAddProductResponse:
 | 
						||
        try:
 | 
						||
            self.grant_access(user, request.deal_id)
 | 
						||
 | 
						||
            deal = await self.session.scalar(select(Deal).where(Deal.id == request.deal_id))
 | 
						||
            if not deal:
 | 
						||
                raise HTTPException(status_code=404, detail="Сделка не найдена")
 | 
						||
            product = await self.session.scalar(
 | 
						||
                select(models.Product).where(models.Product.id == request.product.product.id))
 | 
						||
            if not product:
 | 
						||
                raise HTTPException(status_code=404, detail="Товар не найден")
 | 
						||
            # Preventing duplicates
 | 
						||
            deal_product = await self.session.scalar(
 | 
						||
                select(models.secondary.DealProduct)
 | 
						||
                .where(models.secondary.DealProduct.deal_id == request.deal_id,
 | 
						||
                       models.secondary.DealProduct.product_id == request.product.product.id)
 | 
						||
            )
 | 
						||
            if deal_product:
 | 
						||
                raise HTTPException(status_code=400, detail="Товар уже добавлен")
 | 
						||
            deal_product = models.secondary.DealProduct(
 | 
						||
                deal_id=request.deal_id,
 | 
						||
                product_id=request.product.product.id,
 | 
						||
                quantity=request.product.quantity
 | 
						||
            )
 | 
						||
            self.session.add(deal_product)
 | 
						||
            await self.session.flush()
 | 
						||
            for service in request.product.services:
 | 
						||
                deal_product_service = models.secondary.DealProductService(
 | 
						||
                    deal_id=request.deal_id,
 | 
						||
                    product_id=request.product.product.id,
 | 
						||
                    service_id=service.service.id,
 | 
						||
                    price=service.price
 | 
						||
                )
 | 
						||
                self.session.add(deal_product_service)
 | 
						||
            await self.session.commit()
 | 
						||
            return DealAddProductResponse(ok=True, message='Товар успешно добавлен')
 | 
						||
        except Exception as e:
 | 
						||
            await self.session.rollback()
 | 
						||
            return DealAddProductResponse(ok=False, message=str(e))
 | 
						||
 | 
						||
    async def delete_product(
 | 
						||
            self,
 | 
						||
            user: Union[models.User, dict],
 | 
						||
            request: DealDeleteProductRequest
 | 
						||
    ) -> DealDeleteProductResponse:
 | 
						||
        try:
 | 
						||
            self.grant_access(user, request.deal_id)
 | 
						||
            deal_product = await self.session.scalar(
 | 
						||
                select(models.secondary.DealProduct)
 | 
						||
                .where(models.secondary.DealProduct.deal_id == request.deal_id,
 | 
						||
                       models.secondary.DealProduct.product_id == request.product_id)
 | 
						||
            )
 | 
						||
            if not deal_product:
 | 
						||
                raise HTTPException(status_code=404, detail="Сделка не найдена")
 | 
						||
            await self.session.delete(deal_product)
 | 
						||
            await self.session.commit()
 | 
						||
            return DealDeleteProductResponse(ok=True, message='Товар успешно удален')
 | 
						||
        except Exception as e:
 | 
						||
            await self.session.rollback()
 | 
						||
            return DealDeleteProductResponse(ok=False, message=str(e))
 | 
						||
 | 
						||
    async def delete_products(
 | 
						||
            self,
 | 
						||
            user: Union[models.User, dict],
 | 
						||
            request: DealDeleteProductsRequest
 | 
						||
    ) -> DealDeleteProductsResponse:
 | 
						||
        try:
 | 
						||
            self.grant_access(user, request.deal_id)
 | 
						||
            deal_products = await self.session.scalars(
 | 
						||
                select(models.secondary.DealProduct)
 | 
						||
                .where(models.secondary.DealProduct.deal_id == request.deal_id,
 | 
						||
                       models.secondary.DealProduct.product_id.in_(request.product_ids))
 | 
						||
            )
 | 
						||
            for deal_product in deal_products:
 | 
						||
                await self.session.delete(deal_product)
 | 
						||
            await self.session.commit()
 | 
						||
            return DealDeleteProductsResponse(ok=True, message='Товары успешно удалены')
 | 
						||
        except Exception as e:
 | 
						||
            await self.session.rollback()
 | 
						||
            return DealDeleteProductsResponse(ok=False, message=str(e))
 | 
						||
 | 
						||
    async def update_product(
 | 
						||
            self,
 | 
						||
            user: Union[models.User, dict],
 | 
						||
            request: DealUpdateProductRequest
 | 
						||
    ):
 | 
						||
        try:
 | 
						||
            self.grant_access(user, request.deal_id)
 | 
						||
            deal_product: models.DealProduct = await self.session.scalar(
 | 
						||
                select(models.secondary.DealProduct)
 | 
						||
                .where(models.secondary.DealProduct.deal_id == request.deal_id,
 | 
						||
                       models.secondary.DealProduct.product_id == request.product.product.id)
 | 
						||
            )
 | 
						||
            if not deal_product:
 | 
						||
                raise HTTPException(status_code=404, detail="Указанный товар не найден")
 | 
						||
            # getting new services and deleted
 | 
						||
            database_services = set([service.service_id for service in deal_product.services])
 | 
						||
            request_services = set([service.service.id for service in request.product.services])
 | 
						||
 | 
						||
            new_services = request_services.difference(database_services)
 | 
						||
            deleted_services = database_services.difference(request_services)
 | 
						||
            services_dict = {service.service.id: service for service in request.product.services}
 | 
						||
 | 
						||
            # Deleting and updating existing  services
 | 
						||
            for service in deal_product.services:
 | 
						||
                service: models.DealProductService
 | 
						||
                if service.service_id in deleted_services:
 | 
						||
                    await self.session.delete(service)
 | 
						||
                    await self.session.flush()
 | 
						||
                    continue
 | 
						||
                request_service = services_dict[service.service_id]
 | 
						||
                service.price = request_service.price
 | 
						||
                service.is_fixed_price = request_service.is_fixed_price
 | 
						||
                await self.session.flush()
 | 
						||
 | 
						||
            # Creating services
 | 
						||
            for service in request.product.services:
 | 
						||
                if service.service.id not in new_services:
 | 
						||
                    continue
 | 
						||
                deal_product_service = models.DealProductService(
 | 
						||
                    deal_id=request.deal_id,
 | 
						||
                    product_id=request.product.product.id,
 | 
						||
                    service_id=service.service.id,
 | 
						||
                    price=service.price
 | 
						||
                )
 | 
						||
                self.session.add(deal_product_service)
 | 
						||
                await self.session.flush()
 | 
						||
 | 
						||
            # Updating product
 | 
						||
            deal_product.quantity = request.product.quantity
 | 
						||
 | 
						||
            # Updating deleting old employees
 | 
						||
            delete_stmt = (
 | 
						||
                delete(
 | 
						||
                    models.deal_product_service_employees
 | 
						||
                )
 | 
						||
                .where(
 | 
						||
                    models.deal_product_service_employees.c.deal_id == request.deal_id,
 | 
						||
                    models.deal_product_service_employees.c.service_id.in_(request_services.union(database_services)),
 | 
						||
                    models.deal_product_service_employees.c.product_id == request.product.product.id
 | 
						||
                )
 | 
						||
            )
 | 
						||
            await self.session.execute(delete_stmt)
 | 
						||
            await self.session.flush()
 | 
						||
 | 
						||
            insert_data = []
 | 
						||
            for service in request.product.services:
 | 
						||
                service: DealProductServiceSchema
 | 
						||
                for employee in service.employees:
 | 
						||
                    insert_data.append({
 | 
						||
                        'deal_id': request.deal_id,
 | 
						||
                        'service_id': service.service.id,
 | 
						||
                        'product_id': request.product.product.id,
 | 
						||
                        'user_id': employee.id
 | 
						||
                    })
 | 
						||
            if insert_data:
 | 
						||
                await self.session.execute(insert(models.deal_product_service_employees),
 | 
						||
                                           insert_data)
 | 
						||
                await self.session.flush()
 | 
						||
 | 
						||
            await self.session.commit()
 | 
						||
            return DealUpdateProductResponse(ok=True, message='Товар успешно обновлен')
 | 
						||
 | 
						||
        except Exception as e:
 | 
						||
            await self.session.rollback()
 | 
						||
            return DealUpdateProductResponse(ok=False, message=str(e))
 | 
						||
 | 
						||
    async def add_kit_to_deal_product(
 | 
						||
            self,
 | 
						||
            user: Union[models.User, dict],
 | 
						||
            request: DealProductAddKitRequest
 | 
						||
    ) -> DealProductAddKitResponse:
 | 
						||
        try:
 | 
						||
            self.grant_access(user, request.deal_id)
 | 
						||
            service_service = ServiceService(self.session)
 | 
						||
            kit = await service_service.get_kit_by_id(request.kit_id)
 | 
						||
            if not kit:
 | 
						||
                return DealProductAddKitResponse(ok=False, message='Указанный набор услуг не найден')
 | 
						||
            services: list[models.Service] = kit.services
 | 
						||
            deal_product_stmt = (
 | 
						||
                select(
 | 
						||
                    models.DealProduct
 | 
						||
                )
 | 
						||
                .where(
 | 
						||
                    models.DealProduct.deal_id == request.deal_id,
 | 
						||
                    models.DealProduct.product_id == request.product_id
 | 
						||
                )
 | 
						||
            )
 | 
						||
            deal_product: Optional[models.DealProduct] = await self.session.scalar(deal_product_stmt)
 | 
						||
            if not deal_product:
 | 
						||
                return DealProductAddKitResponse(ok=False, message='Указанный товар не найден')
 | 
						||
            insert_data = []
 | 
						||
            for service in services:
 | 
						||
                service_price = self.get_service_price(service, deal_product.quantity)
 | 
						||
                insert_data.append({
 | 
						||
                    'deal_id': request.deal_id,
 | 
						||
                    'product_id': deal_product.product_id,
 | 
						||
                    'service_id': service.id,
 | 
						||
                    'price': service_price,
 | 
						||
                })
 | 
						||
            if not insert_data:
 | 
						||
                return DealProductAddKitResponse(ok=True, message='Набор услуг успешно добавлен к товару')
 | 
						||
            # Deleting previous DealProductService-s
 | 
						||
            delete_stmt = (
 | 
						||
                delete(
 | 
						||
                    models.DealProductService
 | 
						||
                )
 | 
						||
                .where(
 | 
						||
                    models.DealProductService.product_id == deal_product.product_id,
 | 
						||
                    models.DealProductService.deal_id == request.deal_id
 | 
						||
                )
 | 
						||
            )
 | 
						||
            await self.session.execute(delete_stmt)
 | 
						||
            await self.session.flush()
 | 
						||
 | 
						||
            await self.session.execute(
 | 
						||
                insert(models.DealProductService),
 | 
						||
                insert_data
 | 
						||
            )
 | 
						||
            await self.session.flush()
 | 
						||
            await self.session.commit()
 | 
						||
            return DealProductAddKitResponse(ok=True, message='Набор услуг успешно добавлен к товару')
 | 
						||
        except Exception as e:
 | 
						||
            return DealProductAddKitResponse(ok=False, message=str(e))
 | 
						||
 | 
						||
    # endregion
 | 
						||
 | 
						||
    async def complete(self, user: User, request: DealCompleteRequest) -> DealCompleteResponse:
 | 
						||
        try:
 | 
						||
            # check for admin
 | 
						||
            if not user.is_admin:
 | 
						||
                return DealCompleteResponse(ok=False, message='Завершить сделку может только администратор')
 | 
						||
 | 
						||
            deal = await self._get_deal_by_id(request.deal_id)
 | 
						||
            if not deal:
 | 
						||
                return DealCompleteResponse(ok=False, message="Сделка не найдена")
 | 
						||
            deal.is_completed = True
 | 
						||
            await self.session.commit()
 | 
						||
            return DealCompleteResponse(ok=True, message="Сделка успешно завершена")
 | 
						||
        except Exception as e:
 | 
						||
            await self.session.rollback()
 | 
						||
            return DealCompleteResponse(ok=False, message=str(e))
 | 
						||
 | 
						||
    async def recalculate_price(self, request: DealRecalculatePriceRequest) -> DealRecalculatePriceResponse:
 | 
						||
        try:
 | 
						||
            deal_stmt = (
 | 
						||
                select(
 | 
						||
                    Deal
 | 
						||
                )
 | 
						||
                .options(
 | 
						||
                    selectinload(Deal.services)
 | 
						||
                    .joinedload(models.DealService.service),
 | 
						||
                    selectinload(Deal.products)
 | 
						||
                    .selectinload(DealProduct.services)
 | 
						||
                    .joinedload(DealProductService.service),
 | 
						||
                )
 | 
						||
                .where(Deal.id == request.deal_id)
 | 
						||
            )
 | 
						||
            deal: Deal = await self.session.scalar(deal_stmt)
 | 
						||
            services_quantity = defaultdict(lambda: 0)
 | 
						||
            for product in deal.products:
 | 
						||
                product: DealProduct
 | 
						||
                for service in product.services:
 | 
						||
                    service: DealProductService
 | 
						||
                    if service.is_fixed_price:
 | 
						||
                        continue
 | 
						||
                    services_quantity[service.service_id] += product.quantity
 | 
						||
 | 
						||
            services_prices = {}
 | 
						||
            for product in deal.products:
 | 
						||
                for service in product.services:
 | 
						||
                    if service.is_fixed_price:
 | 
						||
                        continue
 | 
						||
                    quantity = services_quantity[service.service_id]
 | 
						||
                    print(service.service_id, quantity)
 | 
						||
                    if service.service_id in services_prices:
 | 
						||
                        service.price = services_prices[service.service_id]
 | 
						||
                        continue
 | 
						||
                    price = self.get_service_price(
 | 
						||
                        service=service.service,
 | 
						||
                        quantity=quantity
 | 
						||
                    )
 | 
						||
                    print(service.service_id, price)
 | 
						||
                    service.price = price
 | 
						||
                    services_prices[service.service_id] = price
 | 
						||
            for service in deal.services:
 | 
						||
                service: models.DealService
 | 
						||
                if service.is_fixed_price:
 | 
						||
                    continue
 | 
						||
                price = self.get_service_price(
 | 
						||
                    service=service.service,
 | 
						||
                    quantity=service.quantity
 | 
						||
                )
 | 
						||
                service.price = price
 | 
						||
            await self.session.commit()
 | 
						||
            return DealRecalculatePriceResponse(ok=True, message="Цены успешно пересчитаны")
 | 
						||
        except Exception as e:
 | 
						||
            return DealRecalculatePriceResponse(ok=False, message=str(e))
 |