1327 lines
58 KiB
Python
1327 lines
58 KiB
Python
from collections import defaultdict
|
||
|
||
import lexorank
|
||
from fastapi import HTTPException
|
||
from sqlalchemy import select, func, update, delete, insert, and_
|
||
from sqlalchemy.orm import joinedload, selectinload
|
||
from starlette import status
|
||
|
||
from models import User, Service, Client, DealProductService, deal_relations, DealStatusHistory, Product, DealProduct
|
||
import models.deal
|
||
import models.secondary
|
||
from models.deal import *
|
||
from models.deal_group import DealGroup
|
||
from models.shipping import ShippingProduct
|
||
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
|
||
from services import deal_group
|
||
|
||
|
||
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, status_id: int) -> str:
|
||
deal_query = await self.session.execute(
|
||
select(Deal)
|
||
.where(Deal.current_status_id == status_id)
|
||
.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_id: int,
|
||
user: models.User,
|
||
deadline: datetime.datetime = None,
|
||
rank=None,
|
||
comment: str = ''):
|
||
if not deal.current_status_id == status_id:
|
||
deadline = deadline
|
||
status_change = DealStatusHistory(
|
||
deal_id=deal.id,
|
||
user_id=user.id,
|
||
changed_at=datetime.datetime.now(),
|
||
from_status_id=deal.current_status_id,
|
||
to_status_id=status_id,
|
||
next_status_deadline=deadline,
|
||
comment=comment
|
||
)
|
||
self.session.add(status_change)
|
||
deal.current_status_id = status_id
|
||
if not rank:
|
||
rank = await self._get_rank_for_deal(status_id)
|
||
if rank:
|
||
deal.lexorank = rank
|
||
await self.session.flush()
|
||
|
||
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="Сделка не найдена")
|
||
if deal.group:
|
||
await deal_group.DealGroupService(self.session).delete_group(deal.group.id)
|
||
else:
|
||
deal.is_deleted = True
|
||
await self.session.commit()
|
||
return DealDeleteResponse(ok=True, message="Сделка успешно удалена")
|
||
|
||
async def quick_create(self, request: DealQuickCreateRequest, user: models.User) -> DealQuickCreateResponse:
|
||
deal_status = await self.session.get(DealStatus, request.status_id)
|
||
if not deal_status:
|
||
raise HTTPException(status_code=400, detail="Указан некорректный статус")
|
||
|
||
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(request.status_id)
|
||
deal = Deal(
|
||
name=request.name,
|
||
created_at=datetime.datetime.now(),
|
||
client_id=client.id,
|
||
current_status_id=request.status_id,
|
||
board_id=deal_status.board_id,
|
||
lexorank=rank,
|
||
shipping_warehouse_id=shipping_warehouse.id,
|
||
base_marketplace_key=request.base_marketplace.key
|
||
)
|
||
self.session.add(deal)
|
||
await self.session.flush()
|
||
# 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: models.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, 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_all(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_id,
|
||
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),
|
||
joinedload(Deal.status),
|
||
joinedload(Deal.board),
|
||
)
|
||
.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
|
||
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,
|
||
status=deal.status,
|
||
board=deal.board,
|
||
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.status),
|
||
joinedload(Deal.board)
|
||
.joinedload(Board.project),
|
||
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),
|
||
selectinload(Deal.pallets)
|
||
.selectinload(Pallet.shipping_products)
|
||
.selectinload(ShippingProduct.product)
|
||
.noload(Product.barcodes),
|
||
selectinload(Deal.boxes)
|
||
.selectinload(Box.product)
|
||
.noload(Product.barcodes),
|
||
selectinload(Deal.employees)
|
||
.joinedload(DealEmployees.user),
|
||
)
|
||
.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, user: User) -> DealUpdateGeneralInfoResponse:
|
||
try:
|
||
deal: Deal = await self.session.scalar(
|
||
select(Deal)
|
||
.options(
|
||
selectinload(Deal.group)
|
||
.selectinload(DealGroup.deals)
|
||
)
|
||
.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
|
||
|
||
if deal.board_id != request.data.board.id or deal.current_status_id != request.data.status.id:
|
||
if deal.group:
|
||
for deal in deal.group.deals:
|
||
deal.board_id = request.data.board.id
|
||
await self.change_status(deal, request.data.status.id, user)
|
||
else:
|
||
deal.board_id = request.data.board.id
|
||
await self.change_status(deal, request.data.status.id, user)
|
||
|
||
if deal.group:
|
||
for deal in deal.group.deals:
|
||
deal.is_accounted = request.data.is_accounted
|
||
else:
|
||
deal.is_accounted = request.data.is_accounted
|
||
|
||
# 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
|
||
|
||
# Updating manager
|
||
if request.data.manager:
|
||
deal.manager_id = request.data.manager.id
|
||
else:
|
||
deal.manager = None
|
||
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_id == request.status_id,
|
||
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_id, 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,
|
||
comment=old_deal_product.comment,
|
||
)
|
||
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
|
||
deal_product.comment = request.product.comment
|
||
|
||
# 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="Сделка не найдена")
|
||
|
||
if deal.group:
|
||
await deal_group.DealGroupService(self.session).complete_group(deal.group.id)
|
||
else:
|
||
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 get_quantity_dict(self, deals: List[models.Deal]):
|
||
services_quantity = defaultdict(lambda: 0)
|
||
for deal in deals:
|
||
for product in deal.products:
|
||
product: DealProduct
|
||
for service in product.services:
|
||
service: DealProductService
|
||
services_quantity[service.service_id] += product.quantity
|
||
for service in deal.services:
|
||
service: models.DealService
|
||
services_quantity[service.service_id] += service.quantity
|
||
return services_quantity
|
||
|
||
async def _recalculate_price_single(self, deal: models.Deal, quantity_dict: dict):
|
||
services_quantity = quantity_dict
|
||
services_prices = {}
|
||
for product in deal.products:
|
||
for service in product.services:
|
||
if service.is_fixed_price:
|
||
continue
|
||
quantity = services_quantity[service.service_id]
|
||
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
|
||
)
|
||
service.price = price
|
||
services_prices[service.service_id] = price
|
||
for service in deal.services:
|
||
service: models.DealService
|
||
if service.is_fixed_price:
|
||
continue
|
||
quantity = services_quantity[service.service_id]
|
||
price = self.get_service_price(
|
||
service=service.service,
|
||
quantity=quantity
|
||
)
|
||
print(service.service_id, price)
|
||
service.price = price
|
||
|
||
async def _recalculate_price_group(self, group: models.DealGroup):
|
||
deals = await self.session.scalars(
|
||
select(Deal)
|
||
.options(
|
||
selectinload(Deal.services)
|
||
.joinedload(models.DealService.service),
|
||
selectinload(Deal.products)
|
||
.selectinload(DealProduct.services)
|
||
.joinedload(DealProductService.service),
|
||
)
|
||
.where(Deal.group == group)
|
||
)
|
||
deals = list(deals.all())
|
||
services_quantity = await self.get_quantity_dict(deals)
|
||
for deal in deals:
|
||
await self._recalculate_price_single(deal, services_quantity)
|
||
|
||
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),
|
||
joinedload(Deal.group)
|
||
)
|
||
.where(Deal.id == request.deal_id)
|
||
)
|
||
deal: Deal = await self.session.scalar(deal_stmt)
|
||
if not deal.group:
|
||
quantity_dict = await self.get_quantity_dict([deal])
|
||
await self._recalculate_price_single(deal, quantity_dict)
|
||
else:
|
||
await self._recalculate_price_group(deal.group)
|
||
await self.session.commit()
|
||
return DealRecalculatePriceResponse(ok=True, message="Цены успешно пересчитаны")
|
||
except Exception as e:
|
||
return DealRecalculatePriceResponse(ok=False, message=str(e))
|
||
|
||
async def _assign_employee(self, deal: Deal, user: User) -> tuple[bool, str]:
|
||
assigned_employee_ids = [assignment.user_id for assignment in deal.employees]
|
||
if user.id in assigned_employee_ids:
|
||
return False, "Работник уже назначен"
|
||
|
||
assignment = DealEmployees(user_id=user.id, deal_id=deal.id, created_at=datetime.datetime.now())
|
||
self.session.add(assignment)
|
||
await self.session.commit()
|
||
|
||
return True, "Работник успешно назначен"
|
||
|
||
async def _unassign_employee(self, deal: Deal, user: User) -> tuple[bool, str]:
|
||
assigned_employee_ids = [assignment.user_id for assignment in deal.employees]
|
||
if user.id not in assigned_employee_ids:
|
||
return False, "Работник еще не назначен"
|
||
|
||
stmt = delete(DealEmployees).where(and_(DealEmployees.user_id == user.id, DealEmployees.deal_id == deal.id))
|
||
await self.session.execute(stmt)
|
||
await self.session.commit()
|
||
|
||
return True, "Работник успешно удален"
|
||
|
||
async def manage_employee(self, request: ManageEmployeeRequest) -> ManageEmployeeResponse:
|
||
deal: Optional[Deal] = await self._get_deal_by_id(request.deal_id)
|
||
if not deal:
|
||
return ManageEmployeeResponse(ok=False, message=f"Сделка с ID {request.deal_id} не найдена")
|
||
|
||
user: Optional[User] = await self.session.get(User, request.user_id)
|
||
if not user:
|
||
return ManageEmployeeResponse(ok=False, message=f"Пользователь с ID {request.user_id} не найден")
|
||
|
||
if request.is_assign:
|
||
ok, message = await self._assign_employee(deal, user)
|
||
else:
|
||
ok, message = await self._unassign_employee(deal, user)
|
||
|
||
return ManageEmployeeResponse(ok=ok, message=message)
|
||
|
||
async def get_available_employees_to_assign(self, deal_id: int) -> GetAvailableEmployeesToAssignResponse:
|
||
assigned_users = select(DealEmployees.user_id).where(DealEmployees.deal_id == deal_id)
|
||
|
||
stmt_free_employees = (
|
||
select(User)
|
||
.where(and_(
|
||
User.is_deleted == False,
|
||
User.role_key == "employee",
|
||
User.id.not_in(assigned_users),
|
||
))
|
||
)
|
||
|
||
free_employees = (await self.session.execute(stmt_free_employees)).scalars().all()
|
||
return GetAvailableEmployeesToAssignResponse(employees=free_employees)
|
||
|
||
async def _create_deal_from_excel(
|
||
self,
|
||
client: Client,
|
||
deal_status: DealStatus,
|
||
breakdown: CityBreakdownFromExcelSchema,
|
||
user: User,
|
||
) -> Deal:
|
||
rank = await self._get_rank_for_deal(deal_status.id)
|
||
deal = Deal(
|
||
name=f"{client.name} - {breakdown.base_marketplace.key.upper()} - {breakdown.shipping_warehouse.name}",
|
||
created_at=datetime.datetime.now(),
|
||
current_status_id=deal_status.id,
|
||
lexorank=rank,
|
||
client_id=client.id,
|
||
base_marketplace_key=breakdown.base_marketplace.key,
|
||
shipping_warehouse_id=breakdown.shipping_warehouse.id,
|
||
)
|
||
|
||
self.session.add(deal)
|
||
await self.session.flush()
|
||
return deal
|
||
|
||
async def _get_or_create_warehouse(
|
||
self,
|
||
shipping_warehouse: OptionalShippingWarehouseSchema,
|
||
) -> OptionalShippingWarehouseSchema:
|
||
if not shipping_warehouse.id:
|
||
stmt = select(ShippingWarehouse).where(ShippingWarehouse.name == shipping_warehouse.name)
|
||
row = (await self.session.execute(stmt)).first()
|
||
warehouse_model: Optional[ShippingWarehouse] = row[0] if row else None
|
||
if warehouse_model:
|
||
shipping_warehouse.id = warehouse_model.id
|
||
else:
|
||
warehouse = await ShippingWarehouseService(self.session).create_by_name(shipping_warehouse.name)
|
||
shipping_warehouse.id = warehouse.id
|
||
|
||
return shipping_warehouse
|
||
|
||
async def create_deals_from_excel(
|
||
self,
|
||
request: CreateDealsFromExcelRequest,
|
||
user: User,
|
||
) -> CreateDealsFromExcelResponse:
|
||
client: Optional[Client] = await self.session.get(Client, request.client_id)
|
||
if not client:
|
||
return CreateDealsFromExcelResponse(ok=False, message=f"Клиент с ID {request.client_id} не найден")
|
||
|
||
deal_status: Optional[DealStatus] = await self.session.get(DealStatus, request.status_id)
|
||
if not deal_status:
|
||
return CreateDealsFromExcelResponse(ok=False, message=f"Статус с ID {request.status_id} не найден")
|
||
|
||
deals_dict: dict[str, Deal] = {}
|
||
group = await deal_group.DealGroupService(self.session).create_group_model()
|
||
|
||
for product_data in request.products:
|
||
for breakdown in product_data.cities_breakdown:
|
||
breakdown.shipping_warehouse = await self._get_or_create_warehouse(breakdown.shipping_warehouse)
|
||
|
||
key = f"{breakdown.shipping_warehouse.id} - {breakdown.base_marketplace.key}"
|
||
deal = deals_dict.get(key)
|
||
if not deal:
|
||
deal = await self._create_deal_from_excel(client, deal_status, breakdown, user)
|
||
deals_dict[key] = deal
|
||
|
||
insert_stmt = insert(deal_relations).values({
|
||
'deal_id': deal.id,
|
||
'group_id': group.id
|
||
})
|
||
await self.session.execute(insert_stmt)
|
||
|
||
deal_product = DealProduct(
|
||
deal_id=deal.id,
|
||
product_id=product_data.product_id,
|
||
quantity=breakdown.quantity,
|
||
)
|
||
self.session.add(deal_product)
|
||
|
||
await self.session.commit()
|
||
return CreateDealsFromExcelResponse(ok=True, message="Сделки успешно созданы")
|
||
|
||
async def get_deals_grouped(self, deal: models.Deal) -> List[models.Deal]:
|
||
if not deal.group:
|
||
return [deal]
|
||
deals = await self.session.scalars(
|
||
select(Deal)
|
||
.options(
|
||
selectinload(Deal.services)
|
||
.joinedload(models.DealService.service),
|
||
selectinload(Deal.products)
|
||
.selectinload(DealProduct.services)
|
||
.joinedload(DealProductService.service),
|
||
)
|
||
.where(Deal.group == deal.group)
|
||
)
|
||
deals = list(deals.all())
|
||
return deals
|