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 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) 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 async def get_summary(self, full: bool = False) -> DealSummaryResponse: price_subquery = self._get_price_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') ) .options( selectinload(Deal.status_history), joinedload(Deal.client), joinedload(Deal.shipping_warehouse), ) .outerjoin( price_subquery, Deal.id == price_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 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 ) ) 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 # 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) # 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: list[models.DealProductService] = ( await self.session.scalars(source_services_stmt)).all() source_services: list[models.Service] = [dpc.service for dpc in deal_product_services] 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 source_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 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 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