feat: projects and boards
This commit is contained in:
119
services/board.py
Normal file
119
services/board.py
Normal file
@@ -0,0 +1,119 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import select, and_, func
|
||||
|
||||
from models import Board, Deal
|
||||
from schemas.board import *
|
||||
from services.base import BaseService
|
||||
|
||||
|
||||
class BoardService(BaseService):
|
||||
async def _get_boards_for_project(self, project_id: int) -> list[Board]:
|
||||
stmt = (
|
||||
select(Board)
|
||||
.where(
|
||||
and_(
|
||||
Board.is_deleted == False,
|
||||
Board.project_id == project_id,
|
||||
)
|
||||
)
|
||||
.order_by(Board.ordinal_number)
|
||||
)
|
||||
boards = (await self.session.scalars(stmt)).all()
|
||||
return list(boards)
|
||||
|
||||
async def get_boards(self, project_id: int) -> GetBoardsResponse:
|
||||
boards = await self._get_boards_for_project(project_id)
|
||||
return GetBoardsResponse(boards=boards)
|
||||
|
||||
async def create_board(self, request: CreateBoardRequest) -> CreateBoardResponse:
|
||||
boards = await self._get_boards_for_project(request.board.project_id)
|
||||
if len(boards) == 0:
|
||||
ordinal_number = 1
|
||||
else:
|
||||
ordinal_number = boards[-1].ordinal_number + 1
|
||||
|
||||
board = Board(
|
||||
**request.board.model_dump(),
|
||||
created_at=datetime.now(),
|
||||
ordinal_number=ordinal_number,
|
||||
)
|
||||
self.session.add(board)
|
||||
await self.session.commit()
|
||||
|
||||
return CreateBoardResponse(ok=True, message="Доска успешно создана")
|
||||
|
||||
async def _get_board_by_id(self, board_id: int) -> Optional[Board]:
|
||||
return await self.session.get(Board, board_id)
|
||||
|
||||
async def update_board(self, request: UpdateBoardRequest) -> UpdateBoardResponse:
|
||||
board = await self._get_board_by_id(request.board.id)
|
||||
if not board:
|
||||
return UpdateBoardResponse(ok=False, message=f"Доска с ID {request.board.id} не найдена")
|
||||
|
||||
board.name = request.board.name
|
||||
await self.session.commit()
|
||||
return UpdateBoardResponse(ok=True, message="Доска успешно обновлена")
|
||||
|
||||
async def update_board_order(self, request: UpdateBoardOrderRequest) -> UpdateBoardOrderResponse:
|
||||
boards = await self._get_boards_for_project(request.project_id)
|
||||
board_idx = 0
|
||||
while board_idx < len(boards) and boards[board_idx].id != request.board_id:
|
||||
board_idx += 1
|
||||
if board_idx == len(boards):
|
||||
return UpdateBoardOrderResponse(ok=False, message=f"Доска с ID {request.board_id} не найдена в проекте")
|
||||
|
||||
board = boards.pop(board_idx)
|
||||
boards.insert(request.new_ordinal_number - 1, board)
|
||||
new_ordinal_number = 1
|
||||
for board in boards:
|
||||
board.ordinal_number = new_ordinal_number
|
||||
new_ordinal_number += 1
|
||||
|
||||
await self.session.commit()
|
||||
return UpdateBoardOrderResponse(ok=True, message="Порядок досок изменен")
|
||||
|
||||
async def _count_deals_in_progress(self, board_id: int) -> int:
|
||||
stmt = (
|
||||
select(func.count(Deal.id))
|
||||
.where(
|
||||
and_(
|
||||
Deal.board_id == board_id,
|
||||
Deal.is_deleted == False,
|
||||
Deal.is_completed == False,
|
||||
)
|
||||
)
|
||||
)
|
||||
return (await self.session.scalars(stmt)).first()
|
||||
|
||||
async def _count_deals(self, board_id: int) -> int:
|
||||
stmt = (
|
||||
select(func.count(Deal.id))
|
||||
.where(Deal.board_id == board_id)
|
||||
)
|
||||
return (await self.session.scalars(stmt)).first()
|
||||
|
||||
async def delete_board(self, board_id: int) -> DeleteBoardResponse:
|
||||
board = await self._get_board_by_id(board_id)
|
||||
if not board:
|
||||
return DeleteBoardResponse(ok=False, message=f"Доска с ID {board_id} не найдена")
|
||||
|
||||
count_deals_in_progress = await self._count_deals_in_progress(board_id)
|
||||
if count_deals_in_progress != 0:
|
||||
return DeleteBoardResponse(
|
||||
ok=False,
|
||||
message=f"Нельзя удалить доску с активными сделками",
|
||||
)
|
||||
|
||||
count_deals = await self._count_deals(board_id)
|
||||
if count_deals == 0:
|
||||
await self.session.delete(board)
|
||||
else:
|
||||
board.is_deleted = True
|
||||
for status in board.deal_statuses:
|
||||
status.is_deleted = True
|
||||
|
||||
await self.session.commit()
|
||||
|
||||
return DeleteBoardResponse(ok=True, message="Доска успешно удалена")
|
||||
244
services/deal.py
244
services/deal.py
@@ -1,13 +1,14 @@
|
||||
from collections import defaultdict
|
||||
|
||||
import lexorank
|
||||
from attr import dataclass
|
||||
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 import User, Service, Client, DealProductService, deal_relations, GroupBillRequest
|
||||
from models.deal import *
|
||||
from models.deal_group import DealGroup
|
||||
from models.shipping import ShippingProduct
|
||||
@@ -16,9 +17,9 @@ from schemas.deal import *
|
||||
from services.auth import AuthService
|
||||
from services.base import BaseService
|
||||
from services.client import ClientService
|
||||
from services.deal_group import DealGroupService
|
||||
from services.service import ServiceService
|
||||
from services.shipping_warehouse import ShippingWarehouseService
|
||||
from services import deal_group
|
||||
|
||||
|
||||
class DealService(BaseService):
|
||||
@@ -36,9 +37,13 @@ class DealService(BaseService):
|
||||
async def _get_deal_by_id(self, deal_id) -> Union[Deal, None]:
|
||||
return await self.session.get(Deal, deal_id)
|
||||
|
||||
async def _get_rank_for_deal(self, deal_status: DealStatus) -> str:
|
||||
async def _get_rank_for_deal(self, status_id: int) -> str:
|
||||
deal_query = await self.session.execute(
|
||||
select(Deal).where(Deal.current_status == deal_status).order_by(Deal.lexorank.desc()).limit(1))
|
||||
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)
|
||||
@@ -46,59 +51,46 @@ class DealService(BaseService):
|
||||
return str(lexorank.parse(deal.lexorank).next())
|
||||
|
||||
async def change_status(self, deal: Deal,
|
||||
status: DealStatus,
|
||||
user: User,
|
||||
status_id: int,
|
||||
user: models.User,
|
||||
deadline: datetime.datetime = None,
|
||||
rank=None,
|
||||
comment: str = ''):
|
||||
if not deal.current_status == status:
|
||||
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=deal.current_status,
|
||||
to_status=status,
|
||||
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 = status
|
||||
deal.current_status_id = status_id
|
||||
if not rank:
|
||||
rank = await self._get_rank_for_deal(status)
|
||||
rank = await self._get_rank_for_deal(status_id)
|
||||
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="Сделка не найдена")
|
||||
if deal.group:
|
||||
await DealGroupService(self.session).delete_group(deal.group.id)
|
||||
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: User) -> DealQuickCreateResponse:
|
||||
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)
|
||||
|
||||
@@ -108,28 +100,25 @@ class DealService(BaseService):
|
||||
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)
|
||||
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=DealStatus.CREATED,
|
||||
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()
|
||||
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(
|
||||
@@ -141,12 +130,12 @@ class DealService(BaseService):
|
||||
await self.session.commit()
|
||||
return DealQuickCreateResponse(deal_id=deal.id)
|
||||
|
||||
async def change_status_manual(self, request: DealChangeStatusRequest, user: User) -> DealChangeStatusResponse:
|
||||
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, DealStatus(request.new_status), user)
|
||||
await self.change_status(deal, request.new_status, user)
|
||||
await self.session.commit()
|
||||
return DealChangeStatusResponse(ok=True)
|
||||
|
||||
@@ -199,7 +188,7 @@ class DealService(BaseService):
|
||||
Deal,
|
||||
func.coalesce(price_subquery.c.total_price, 0),
|
||||
func.row_number().over(
|
||||
partition_by=Deal.current_status,
|
||||
partition_by=Deal.current_status_id,
|
||||
order_by=Deal.lexorank
|
||||
).label('rank'),
|
||||
func.coalesce(products_quantity_subquery.c.total_quantity, 0)
|
||||
@@ -208,7 +197,9 @@ class DealService(BaseService):
|
||||
selectinload(Deal.status_history),
|
||||
joinedload(Deal.client),
|
||||
joinedload(Deal.shipping_warehouse),
|
||||
joinedload(Deal.bill_request)
|
||||
joinedload(Deal.bill_request),
|
||||
joinedload(Deal.status),
|
||||
joinedload(Deal.board),
|
||||
)
|
||||
.outerjoin(
|
||||
price_subquery, Deal.id == price_subquery.c.deal_id,
|
||||
@@ -231,8 +222,6 @@ class DealService(BaseService):
|
||||
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)
|
||||
@@ -242,9 +231,8 @@ class DealService(BaseService):
|
||||
id=deal.id,
|
||||
client_name=deal.client.name,
|
||||
name=deal.name,
|
||||
changed_at=last_status.changed_at,
|
||||
deadline=deadline,
|
||||
status=last_status.to_status,
|
||||
status=deal.status,
|
||||
board=deal.board,
|
||||
total_price=total_price,
|
||||
rank=rank,
|
||||
base_marketplace=base_marketplace,
|
||||
@@ -308,6 +296,9 @@ class DealService(BaseService):
|
||||
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),
|
||||
@@ -350,7 +341,7 @@ class DealService(BaseService):
|
||||
raise HTTPException(status_code=404, detail="Сделка не найдена")
|
||||
return DealSchema.model_validate(deal)
|
||||
|
||||
async def update_general_info(self, request: DealUpdateGeneralInfoRequest) -> DealUpdateGeneralInfoResponse:
|
||||
async def update_general_info(self, request: DealUpdateGeneralInfoRequest, user: User) -> DealUpdateGeneralInfoResponse:
|
||||
try:
|
||||
deal: Deal = await self.session.scalar(
|
||||
select(Deal)
|
||||
@@ -369,6 +360,15 @@ class DealService(BaseService):
|
||||
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
|
||||
@@ -402,7 +402,7 @@ class DealService(BaseService):
|
||||
stmt = (
|
||||
select(Deal)
|
||||
.where(
|
||||
Deal.current_status == request.status,
|
||||
Deal.current_status_id == request.status_id,
|
||||
Deal.id != request.deal_id,
|
||||
Deal.is_deleted == False,
|
||||
Deal.is_completed == False
|
||||
@@ -433,7 +433,7 @@ class DealService(BaseService):
|
||||
else:
|
||||
new_rank = lexorank.middle(lexorank.Bucket.BUCEKT_0)
|
||||
|
||||
await self.change_status(deal, request.status, user,
|
||||
await self.change_status(deal, request.status_id, user,
|
||||
deadline=request.deadline,
|
||||
comment=request.comment,
|
||||
rank=str(new_rank))
|
||||
@@ -1079,12 +1079,9 @@ class DealService(BaseService):
|
||||
return DealCompleteResponse(ok=False, message="Сделка не найдена")
|
||||
|
||||
if deal.group:
|
||||
deals = await DealGroupService(self.session).complete_group(deal.group.id)
|
||||
for completed_deal in deals:
|
||||
await self.change_status(completed_deal, DealStatus.COMPLETED, user)
|
||||
await deal_group.DealGroupService(self.session).complete_group(deal.group.id)
|
||||
else:
|
||||
deal.is_completed = True
|
||||
await self.change_status(deal, DealStatus.COMPLETED, user)
|
||||
await self.session.commit()
|
||||
|
||||
return DealCompleteResponse(ok=True, message="Сделка успешно завершена")
|
||||
@@ -1234,14 +1231,15 @@ class DealService(BaseService):
|
||||
async def _create_deal_from_excel(
|
||||
self,
|
||||
client: Client,
|
||||
deal_status: DealStatus,
|
||||
breakdown: CityBreakdownFromExcelSchema,
|
||||
user: User,
|
||||
) -> Deal:
|
||||
rank = await self._get_rank_for_deal(DealStatus.CREATED)
|
||||
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=DealStatus.CREATED,
|
||||
current_status_id=deal_status.id,
|
||||
lexorank=rank,
|
||||
client_id=client.id,
|
||||
base_marketplace_key=breakdown.base_marketplace.key,
|
||||
@@ -1250,19 +1248,8 @@ class DealService(BaseService):
|
||||
|
||||
self.session.add(deal)
|
||||
await self.session.flush()
|
||||
await self.change_status(deal, DealStatus.AWAITING_ACCEPTANCE, user)
|
||||
|
||||
return deal
|
||||
|
||||
async def _create_group(self) -> DealGroup:
|
||||
group = models.DealGroup(
|
||||
name='',
|
||||
lexorank=lexorank.middle(lexorank.Bucket.BUCEKT_0).__str__(),
|
||||
)
|
||||
self.session.add(group)
|
||||
await self.session.flush()
|
||||
return group
|
||||
|
||||
async def _get_or_create_warehouse(
|
||||
self,
|
||||
shipping_warehouse: OptionalShippingWarehouseSchema,
|
||||
@@ -1288,8 +1275,12 @@ class DealService(BaseService):
|
||||
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 self._create_group()
|
||||
group = await deal_group.DealGroupService(self.session).create_group_model()
|
||||
|
||||
for product_data in request.products:
|
||||
for breakdown in product_data.cities_breakdown:
|
||||
@@ -1298,7 +1289,7 @@ class DealService(BaseService):
|
||||
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, breakdown, user)
|
||||
deal = await self._create_deal_from_excel(client, deal_status, breakdown, user)
|
||||
deals_dict[key] = deal
|
||||
|
||||
insert_stmt = insert(deal_relations).values({
|
||||
@@ -1317,121 +1308,6 @@ class DealService(BaseService):
|
||||
await self.session.commit()
|
||||
return CreateDealsFromExcelResponse(ok=True, message="Сделки успешно созданы")
|
||||
|
||||
async def add_to_group(self, user: User, request: DealAddToGroupRequest) -> DealAddToGroupResponse:
|
||||
try:
|
||||
group_bill_request = await self.session.get(GroupBillRequest, request.group_id)
|
||||
if group_bill_request:
|
||||
raise Exception("Нельзя добавить сделку, так как на группу выставлен счёт.")
|
||||
|
||||
# changing status if needed
|
||||
deal_id = await self.session.scalar(
|
||||
select(deal_relations.c.deal_id)
|
||||
.where(deal_relations.c.group_id == request.group_id)
|
||||
)
|
||||
group_deal_status = await self.session.scalar(
|
||||
select(Deal.current_status)
|
||||
.where(Deal.id == deal_id)
|
||||
)
|
||||
request_deal = await self.session.scalar(
|
||||
select(Deal).where(Deal.id == request.deal_id)
|
||||
)
|
||||
if group_deal_status != request_deal.current_status:
|
||||
await self.change_status(request_deal, group_deal_status, user)
|
||||
insert_stmt = insert(deal_relations).values({
|
||||
'deal_id': request.deal_id,
|
||||
'group_id': request.group_id
|
||||
})
|
||||
await self.session.execute(insert_stmt)
|
||||
await self.session.commit()
|
||||
|
||||
return DealAddToGroupResponse(ok=True, message="Сделка успешно добавлена в группу")
|
||||
except Exception as e:
|
||||
await self.session.rollback()
|
||||
return DealAddToGroupResponse(ok=False, message=str(e))
|
||||
|
||||
async def create_group(self, user: User, request: DealCreateGroupRequest) -> DealCreateGroupResponse:
|
||||
try:
|
||||
# getting lexorank for grop
|
||||
|
||||
group = await self._create_group()
|
||||
|
||||
for deal_id in [request.dragging_deal_id, request.hovered_deal_id]:
|
||||
insert_stmt = insert(deal_relations).values({
|
||||
'deal_id': deal_id,
|
||||
'group_id': group.id
|
||||
})
|
||||
await self.session.execute(insert_stmt)
|
||||
# changing status if needed on draggable deal
|
||||
dragging_deal = await self.session.scalar(
|
||||
select(Deal).where(Deal.id == request.dragging_deal_id)
|
||||
)
|
||||
dropped_deal = await self.session.scalar(
|
||||
select(Deal).where(Deal.id == request.hovered_deal_id)
|
||||
)
|
||||
if dragging_deal.current_status != dropped_deal.current_status:
|
||||
await self.change_status(dragging_deal, DealStatus(dropped_deal.current_status), user)
|
||||
await self.session.commit()
|
||||
return DealCreateGroupResponse(ok=True, message="Группа успешно создана")
|
||||
except Exception as e:
|
||||
return DealCreateGroupResponse(ok=False, message=str(e))
|
||||
|
||||
async def remove_from_group(self, request: DealRemoveFromGroupRequest) -> DealRemoveFromGroupResponse:
|
||||
try:
|
||||
delete_stmt = (
|
||||
delete(deal_relations)
|
||||
.where(
|
||||
deal_relations.c.deal_id == request.deal_id,
|
||||
)
|
||||
)
|
||||
await self.session.execute(delete_stmt)
|
||||
await self.session.commit()
|
||||
return DealRemoveFromGroupResponse(ok=True, message="Сделка успешно удалена из группы")
|
||||
except Exception as e:
|
||||
await self.session.rollback()
|
||||
return DealRemoveFromGroupResponse(ok=False, message=str(e))
|
||||
|
||||
async def update_group(self, request: DealGroupUpdateRequest) -> DealGroupUpdateResponse:
|
||||
try:
|
||||
group = await self.session.scalar(
|
||||
select(models.DealGroup).where(models.DealGroup.id == request.data.id)
|
||||
)
|
||||
if not group:
|
||||
return DealGroupUpdateResponse(ok=False, message="Группа не найдена")
|
||||
# update by dictionary
|
||||
request_dict = request.data.dict()
|
||||
update_stmt = (
|
||||
update(
|
||||
models.DealGroup
|
||||
)
|
||||
.where(models.DealGroup.id == request.data.id)
|
||||
.values(**request_dict)
|
||||
)
|
||||
await self.session.execute(update_stmt)
|
||||
await self.session.commit()
|
||||
return DealGroupUpdateResponse(ok=True, message="Группа успешно обновлена")
|
||||
except Exception as e:
|
||||
await self.session.rollback()
|
||||
return DealGroupUpdateResponse(ok=False, message=str(e))
|
||||
|
||||
async def change_group_status(self, user: User,
|
||||
request: DealGroupChangeStatusRequest) -> DealGroupChangeStatusResponse:
|
||||
try:
|
||||
# getting all deals in group
|
||||
deals = await self.session.scalars(
|
||||
select(deal_relations.c.deal_id)
|
||||
.where(deal_relations.c.group_id == request.group_id)
|
||||
)
|
||||
for deal_id in deals:
|
||||
deal = await self.session.scalar(
|
||||
select(Deal).where(Deal.id == deal_id)
|
||||
)
|
||||
await self.change_status(deal, DealStatus(request.new_status), user)
|
||||
await self.session.commit()
|
||||
return DealGroupChangeStatusResponse(ok=True, message="Статус группы успешно изменен")
|
||||
except Exception as e:
|
||||
await self.session.rollback()
|
||||
return DealGroupChangeStatusResponse(ok=False, message=str(e))
|
||||
|
||||
async def get_deals_grouped(self, deal: models.Deal) -> List[models.Deal]:
|
||||
if not deal.group:
|
||||
return [deal]
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
from sqlalchemy import select
|
||||
from lexorank import lexorank
|
||||
from sqlalchemy import select, insert, update, delete
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
from models import DealService as DealServiceModel
|
||||
from models.deal_group import DealGroup
|
||||
from schemas.deal import *
|
||||
from models import DealService as DealServiceModel, User, Deal, DealProduct, Product, GroupBillRequest
|
||||
from models.deal_group import DealGroup, deal_relations
|
||||
from schemas.group import *
|
||||
from services.base import BaseService
|
||||
from services.deal import DealService
|
||||
|
||||
|
||||
class DealGroupService(BaseService):
|
||||
@@ -25,6 +27,63 @@ class DealGroupService(BaseService):
|
||||
)
|
||||
return group.deals if group else []
|
||||
|
||||
async def create_group_model(self) -> DealGroup:
|
||||
group = DealGroup(
|
||||
name='',
|
||||
lexorank=lexorank.middle(lexorank.Bucket.BUCEKT_0).__str__(),
|
||||
)
|
||||
self.session.add(group)
|
||||
await self.session.flush()
|
||||
return group
|
||||
|
||||
async def create_group(self, user: User, request: DealCreateGroupRequest) -> DealCreateGroupResponse:
|
||||
try:
|
||||
group = await self.create_group_model()
|
||||
|
||||
for deal_id in [request.dragging_deal_id, request.hovered_deal_id]:
|
||||
insert_stmt = insert(deal_relations).values({
|
||||
'deal_id': deal_id,
|
||||
'group_id': group.id
|
||||
})
|
||||
await self.session.execute(insert_stmt)
|
||||
# changing status if needed on draggable deal
|
||||
dragging_deal = await self.session.scalar(
|
||||
select(Deal).where(Deal.id == request.dragging_deal_id)
|
||||
)
|
||||
dropped_deal = await self.session.scalar(
|
||||
select(Deal).where(Deal.id == request.hovered_deal_id)
|
||||
)
|
||||
if dragging_deal.current_status_id != dropped_deal.current_status_id:
|
||||
deal_service = DealService(self.session)
|
||||
await deal_service.change_status(dragging_deal, dropped_deal.current_status_id, user)
|
||||
await self.session.commit()
|
||||
return DealCreateGroupResponse(ok=True, message="Группа успешно создана")
|
||||
except Exception as e:
|
||||
return DealCreateGroupResponse(ok=False, message=str(e))
|
||||
|
||||
async def update_group(self, request: DealGroupUpdateRequest) -> DealGroupUpdateResponse:
|
||||
try:
|
||||
group = await self.session.scalar(
|
||||
select(DealGroup).where(DealGroup.id == request.data.id)
|
||||
)
|
||||
if not group:
|
||||
return DealGroupUpdateResponse(ok=False, message="Группа не найдена")
|
||||
# update by dictionary
|
||||
request_dict = request.data.model_dump()
|
||||
request_dict.pop("bill_request", None)
|
||||
|
||||
update_stmt = (
|
||||
update(DealGroup)
|
||||
.where(DealGroup.id == request.data.id)
|
||||
.values(**request_dict)
|
||||
)
|
||||
await self.session.execute(update_stmt)
|
||||
await self.session.commit()
|
||||
return DealGroupUpdateResponse(ok=True, message="Группа успешно обновлена")
|
||||
except Exception as e:
|
||||
await self.session.rollback()
|
||||
return DealGroupUpdateResponse(ok=False, message=str(e))
|
||||
|
||||
async def complete_group(self, group_id: int) -> list[Deal]:
|
||||
deals = await self.get_deals_by_group_id(group_id)
|
||||
for deal in deals:
|
||||
@@ -36,3 +95,74 @@ class DealGroupService(BaseService):
|
||||
for deal in deals:
|
||||
deal.is_deleted = True
|
||||
await self.session.commit()
|
||||
|
||||
async def change_group_status(
|
||||
self,
|
||||
user: User,
|
||||
request: DealGroupChangeStatusRequest,
|
||||
) -> DealGroupChangeStatusResponse:
|
||||
try:
|
||||
# getting all deals in group
|
||||
deals = await self.session.scalars(
|
||||
select(deal_relations.c.deal_id)
|
||||
.where(deal_relations.c.group_id == request.group_id)
|
||||
)
|
||||
|
||||
deal_service = DealService(self.session)
|
||||
for deal_id in deals:
|
||||
deal = await self.session.scalar(
|
||||
select(Deal).where(Deal.id == deal_id)
|
||||
)
|
||||
await deal_service.change_status(deal, request.new_status, user)
|
||||
await self.session.commit()
|
||||
return DealGroupChangeStatusResponse(ok=True, message="Статус группы успешно изменен")
|
||||
except Exception as e:
|
||||
await self.session.rollback()
|
||||
return DealGroupChangeStatusResponse(ok=False, message=str(e))
|
||||
|
||||
async def add_deal(self, user: User, request: DealAddToGroupRequest) -> DealAddToGroupResponse:
|
||||
try:
|
||||
group_bill_request = await self.session.get(GroupBillRequest, request.group_id)
|
||||
if group_bill_request:
|
||||
raise Exception("Нельзя добавить сделку, так как на группу выставлен счёт.")
|
||||
|
||||
# changing status if needed
|
||||
deal_id = await self.session.scalar(
|
||||
select(deal_relations.c.deal_id)
|
||||
.where(deal_relations.c.group_id == request.group_id)
|
||||
)
|
||||
group_deal_status = await self.session.scalar(
|
||||
select(Deal.current_status_id)
|
||||
.where(Deal.id == deal_id)
|
||||
)
|
||||
request_deal = await self.session.scalar(
|
||||
select(Deal).where(Deal.id == request.deal_id)
|
||||
)
|
||||
if group_deal_status != request_deal.current_status_id:
|
||||
await DealService(self.session).change_status(request_deal, group_deal_status, user)
|
||||
insert_stmt = insert(deal_relations).values({
|
||||
'deal_id': request.deal_id,
|
||||
'group_id': request.group_id
|
||||
})
|
||||
await self.session.execute(insert_stmt)
|
||||
await self.session.commit()
|
||||
|
||||
return DealAddToGroupResponse(ok=True, message="Сделка успешно добавлена в группу")
|
||||
except Exception as e:
|
||||
await self.session.rollback()
|
||||
return DealAddToGroupResponse(ok=False, message=str(e))
|
||||
|
||||
async def remove_deal(self, request: DealRemoveFromGroupRequest) -> DealRemoveFromGroupResponse:
|
||||
try:
|
||||
delete_stmt = (
|
||||
delete(deal_relations)
|
||||
.where(
|
||||
deal_relations.c.deal_id == request.deal_id,
|
||||
)
|
||||
)
|
||||
await self.session.execute(delete_stmt)
|
||||
await self.session.commit()
|
||||
return DealRemoveFromGroupResponse(ok=True, message="Сделка успешно удалена из группы")
|
||||
except Exception as e:
|
||||
await self.session.rollback()
|
||||
return DealRemoveFromGroupResponse(ok=False, message=str(e))
|
||||
|
||||
73
services/project.py
Normal file
73
services/project.py
Normal file
@@ -0,0 +1,73 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import select, update, func, delete, and_
|
||||
|
||||
from models import Project, Board
|
||||
from schemas.project import *
|
||||
from services.base import BaseService
|
||||
|
||||
|
||||
class ProjectService(BaseService):
|
||||
async def get_projects(self) -> GetProjectsResponse:
|
||||
boards_sub = (
|
||||
select(Board)
|
||||
.where(Board.is_deleted == False)
|
||||
.subquery()
|
||||
)
|
||||
stmt = (
|
||||
select(
|
||||
Project.id,
|
||||
Project.name,
|
||||
func.count(boards_sub.c.id)
|
||||
)
|
||||
.join(boards_sub, Project.id == boards_sub.c.project_id, isouter=True)
|
||||
.where(Project.is_deleted == False)
|
||||
.group_by(Project.id, Project.name)
|
||||
.order_by(Project.name)
|
||||
)
|
||||
project_data = (await self.session.execute(stmt)).all()
|
||||
|
||||
projects = []
|
||||
for project_id, name, boards_count in project_data:
|
||||
project = ProjectSchemaWithCount(id=project_id, name=name, boards_count=boards_count)
|
||||
projects.append(project)
|
||||
|
||||
return GetProjectsResponse(projects=projects)
|
||||
|
||||
async def create_project(self, request: CreateProjectRequest) -> CreateProjectResponse:
|
||||
project = Project(
|
||||
name=request.project.name,
|
||||
created_at=datetime.now(),
|
||||
)
|
||||
self.session.add(project)
|
||||
await self.session.commit()
|
||||
return UpdateProjectResponse(ok=True, message="Проект успешно создан")
|
||||
|
||||
async def update_project(self, request: UpdateProjectRequest) -> UpdateProjectResponse:
|
||||
stmt = (
|
||||
update(Project)
|
||||
.where(Project.id == request.project.id)
|
||||
.values(name=request.project.name)
|
||||
)
|
||||
await self.session.execute(stmt)
|
||||
await self.session.commit()
|
||||
|
||||
return UpdateProjectResponse(ok=True, message="Проект успешно изменен")
|
||||
|
||||
async def delete_project(self, project_id: int) -> DeleteProjectResponse:
|
||||
stmt_boards = select(Board).where(Board.project_id == project_id)
|
||||
boards = (await self.session.scalars(stmt_boards)).all()
|
||||
if len(boards) == 0:
|
||||
stmt = (
|
||||
delete(Project)
|
||||
.where(Project.id == project_id)
|
||||
)
|
||||
else:
|
||||
stmt = (
|
||||
update(Project)
|
||||
.where(Project.id == project_id)
|
||||
.values(is_deleted=True)
|
||||
)
|
||||
await self.session.execute(stmt)
|
||||
await self.session.commit()
|
||||
return DeleteProjectResponse(ok=True, message="Проект успешно удален")
|
||||
@@ -5,7 +5,7 @@ from sqlalchemy import select, and_, union_all, func, Subquery, literal
|
||||
|
||||
from enums.profit_table_group_by import ProfitTableGroupBy
|
||||
from models import DealService, Deal, DealStatusHistory, DealProductService, DealProduct, Service, Client, \
|
||||
ShippingWarehouse, BaseMarketplace, User
|
||||
ShippingWarehouse, BaseMarketplace, User, Project, Board
|
||||
from schemas.statistics import GetProfitChartDataResponse, GetProfitChartDataRequest, ProfitChartDataItem, \
|
||||
GetProfitTableDataResponse, GetProfitTableDataRequest, ProfitTableDataItem, CommonProfitFilters
|
||||
from services.base import BaseService
|
||||
@@ -24,7 +24,7 @@ class ProfitStatisticsService(BaseService):
|
||||
'day',
|
||||
Deal.created_at,
|
||||
).label('date'),
|
||||
Deal.current_status,
|
||||
Deal.current_status_id,
|
||||
)
|
||||
.subquery()
|
||||
)
|
||||
@@ -51,7 +51,7 @@ class ProfitStatisticsService(BaseService):
|
||||
'day',
|
||||
last_statuses.c.changed_at,
|
||||
).label('date'),
|
||||
Deal.current_status,
|
||||
Deal.current_status_id,
|
||||
)
|
||||
.join(last_statuses, last_statuses.c.deal_id == Deal.id)
|
||||
.subquery()
|
||||
@@ -97,8 +97,7 @@ class ProfitStatisticsService(BaseService):
|
||||
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def _get_stmt_deal_services(sub_filtered_status_history: Subquery):
|
||||
def _get_stmt_deal_services(self, sub_filtered_status_history: Subquery):
|
||||
return (
|
||||
select(
|
||||
Deal.id.label("deal_id"),
|
||||
@@ -112,10 +111,24 @@ class ProfitStatisticsService(BaseService):
|
||||
.join(DealService, Deal.id == DealService.deal_id)
|
||||
.join(Service, DealService.service_id == Service.id)
|
||||
.join(sub_filtered_status_history, Deal.id == sub_filtered_status_history.c.deal_id)
|
||||
.where(and_(Deal.is_deleted == False, Deal.is_accounted == True))
|
||||
.where(
|
||||
and_(
|
||||
Deal.is_deleted == False,
|
||||
Deal.is_accounted == True,
|
||||
Deal.is_completed == True if self.is_completed_only else True
|
||||
)
|
||||
)
|
||||
.group_by(Deal.id, "date")
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _board_ids_for_project(project_id: int, stmt):
|
||||
board_ids_stmt = (
|
||||
select(Board.id)
|
||||
.where(Board.project_id == project_id)
|
||||
)
|
||||
return stmt.where(Deal.board_id.in_(board_ids_stmt))
|
||||
|
||||
@staticmethod
|
||||
def _apply_filters(request: CommonProfitFilters, stmt_deal_services, stmt_deal_product_services):
|
||||
if request.client_id != -1:
|
||||
@@ -127,9 +140,21 @@ class ProfitStatisticsService(BaseService):
|
||||
stmt_deal_product_services = stmt_deal_product_services.where(
|
||||
Deal.base_marketplace_key == request.base_marketplace_key)
|
||||
|
||||
if request.deal_status_id != -1:
|
||||
stmt_deal_services = stmt_deal_services.where(Deal.current_status == request.deal_status_id)
|
||||
stmt_deal_product_services = stmt_deal_product_services.where(Deal.current_status == request.deal_status_id)
|
||||
if request.project_id != -1:
|
||||
stmt_deal_services = ProfitStatisticsService._board_ids_for_project(request.project_id, stmt_deal_services)
|
||||
stmt_deal_product_services = ProfitStatisticsService._board_ids_for_project(
|
||||
request.project_id,
|
||||
stmt_deal_product_services,
|
||||
)
|
||||
|
||||
if request.board_id != -1:
|
||||
stmt_deal_services = stmt_deal_services.where(Deal.board_id == request.board_id)
|
||||
stmt_deal_product_services = stmt_deal_product_services.where(Deal.board_id == request.board_id)
|
||||
|
||||
if request.deal_status_id != -1:
|
||||
stmt_deal_services = stmt_deal_services.where(Deal.current_status_id == request.deal_status_id)
|
||||
stmt_deal_product_services = stmt_deal_product_services.where(
|
||||
Deal.current_status_id == request.deal_status_id)
|
||||
|
||||
if request.manager_id != -1:
|
||||
stmt_deal_services = stmt_deal_services.where(Deal.manager_id == request.manager_id)
|
||||
@@ -137,8 +162,7 @@ class ProfitStatisticsService(BaseService):
|
||||
|
||||
return stmt_deal_services, stmt_deal_product_services
|
||||
|
||||
@staticmethod
|
||||
def _get_stmt_product_services():
|
||||
def _get_stmt_product_services(self):
|
||||
return (
|
||||
select(
|
||||
Deal.id.label("deal_id"),
|
||||
@@ -154,7 +178,13 @@ class ProfitStatisticsService(BaseService):
|
||||
)
|
||||
)
|
||||
.join(Service, DealProductService.service_id == Service.id)
|
||||
.where(and_(Deal.is_deleted == False, Deal.is_accounted == True))
|
||||
.where(
|
||||
and_(
|
||||
Deal.is_deleted == False,
|
||||
Deal.is_accounted == True,
|
||||
Deal.is_completed == True if self.is_completed_only else True,
|
||||
)
|
||||
)
|
||||
.group_by(Deal.id)
|
||||
)
|
||||
|
||||
@@ -226,16 +256,47 @@ class ProfitStatisticsService(BaseService):
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _join_and_group_by_statuses(stmt):
|
||||
def _join_and_group_by_projects(stmt):
|
||||
return (
|
||||
select(
|
||||
Deal.current_status.label("grouped_value"),
|
||||
Project.id,
|
||||
Project.name.label("grouped_value"),
|
||||
func.count(stmt.c.deal_id).label("deals_count"),
|
||||
func.sum(stmt.c.revenue).label("revenue"),
|
||||
func.sum(stmt.c.profit).label("profit"),
|
||||
)
|
||||
.join(Deal, Deal.id == stmt.c.deal_id)
|
||||
.group_by(Deal.current_status)
|
||||
.join(Board, Board.id == Deal.board_id)
|
||||
.join(Project, Project.id == Board.project_id)
|
||||
.group_by(Project.id, Project.name)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _join_and_group_by_boards(stmt):
|
||||
return (
|
||||
select(
|
||||
Board.id,
|
||||
Board.name.label("grouped_value"),
|
||||
func.count(stmt.c.deal_id).label("deals_count"),
|
||||
func.sum(stmt.c.revenue).label("revenue"),
|
||||
func.sum(stmt.c.profit).label("profit"),
|
||||
)
|
||||
.join(Deal, Deal.id == stmt.c.deal_id)
|
||||
.join(Board, Board.id == Deal.board_id)
|
||||
.group_by(Board.id, Board.name)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _join_and_group_by_statuses(stmt):
|
||||
return (
|
||||
select(
|
||||
Deal.current_status_id.label("grouped_value"),
|
||||
func.count(stmt.c.deal_id).label("deals_count"),
|
||||
func.sum(stmt.c.revenue).label("revenue"),
|
||||
func.sum(stmt.c.profit).label("profit"),
|
||||
)
|
||||
.join(Deal, Deal.id == stmt.c.deal_id)
|
||||
.group_by(Deal.current_status_id)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@@ -320,6 +381,7 @@ class ProfitStatisticsService(BaseService):
|
||||
|
||||
async def _get_data_grouped_by_date(self, request: CommonProfitFilters, is_chart: bool = True):
|
||||
self.date_from, self.date_to = request.date_range
|
||||
self.is_completed_only = request.is_completed_only
|
||||
self.filters = request
|
||||
|
||||
sub_deals_dates = self._get_deals_dates(request.deal_status_id)
|
||||
@@ -399,6 +461,18 @@ class ProfitStatisticsService(BaseService):
|
||||
|
||||
return await self._table_data_from_stmt(stmt_grouped_by_clients)
|
||||
|
||||
async def _get_table_grouped_by_projects(self, request: GetProfitTableDataRequest) -> GetProfitTableDataResponse:
|
||||
sub_grouped_by_deals = self._get_common_table_grouped(request)
|
||||
stmt_grouped_by_projects = self._join_and_group_by_projects(sub_grouped_by_deals)
|
||||
|
||||
return await self._table_data_from_stmt(stmt_grouped_by_projects)
|
||||
|
||||
async def _get_table_grouped_by_boards(self, request: GetProfitTableDataRequest) -> GetProfitTableDataResponse:
|
||||
sub_grouped_by_deals = self._get_common_table_grouped(request)
|
||||
stmt_grouped_by_boards = self._join_and_group_by_boards(sub_grouped_by_deals)
|
||||
|
||||
return await self._table_data_from_stmt(stmt_grouped_by_boards)
|
||||
|
||||
async def _get_table_grouped_by_statuses(self, request: GetProfitTableDataRequest) -> GetProfitTableDataResponse:
|
||||
date_from, date_to = request.date_range
|
||||
|
||||
@@ -449,6 +523,10 @@ class ProfitStatisticsService(BaseService):
|
||||
return await self._get_table_grouped_by_dates(request)
|
||||
case ProfitTableGroupBy.BY_CLIENTS:
|
||||
return await self._get_table_grouped_by_clients(request)
|
||||
case ProfitTableGroupBy.BY_PROJECTS:
|
||||
return await self._get_table_grouped_by_projects(request)
|
||||
case ProfitTableGroupBy.BY_BOARDS:
|
||||
return await self._get_table_grouped_by_boards(request)
|
||||
case ProfitTableGroupBy.BY_STATUSES:
|
||||
return await self._get_table_grouped_by_statuses(request)
|
||||
case ProfitTableGroupBy.BY_WAREHOUSES:
|
||||
|
||||
128
services/status.py
Normal file
128
services/status.py
Normal file
@@ -0,0 +1,128 @@
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import select, and_, func
|
||||
|
||||
from models import DealStatus, Deal
|
||||
from schemas.status import *
|
||||
from services.base import BaseService
|
||||
|
||||
|
||||
class StatusService(BaseService):
|
||||
async def _get_statuses_for_board(self, board_id: int) -> list[DealStatus]:
|
||||
stmt = (
|
||||
select(DealStatus)
|
||||
.where(
|
||||
and_(
|
||||
DealStatus.board_id == board_id,
|
||||
DealStatus.is_deleted == False,
|
||||
)
|
||||
)
|
||||
.order_by(DealStatus.ordinal_number)
|
||||
)
|
||||
statuses = (await self.session.scalars(stmt)).all()
|
||||
return list(statuses)
|
||||
|
||||
async def _get_status_by_id(self, status_id: int) -> Optional[DealStatus]:
|
||||
stmt = (
|
||||
select(DealStatus)
|
||||
.where(DealStatus.id == status_id)
|
||||
)
|
||||
status = await self.session.scalar(stmt)
|
||||
return status
|
||||
|
||||
async def create_status(self, request: CreateStatusRequest) -> CreateStatusResponse:
|
||||
statuses = await self._get_statuses_for_board(request.status.board_id)
|
||||
if len(statuses) == 0:
|
||||
ordinal_number = 1
|
||||
else:
|
||||
statuses[-1].is_finishing = False
|
||||
ordinal_number = statuses[-1].ordinal_number + 1
|
||||
|
||||
status = DealStatus(
|
||||
**request.status.model_dump(),
|
||||
ordinal_number=ordinal_number,
|
||||
is_finishing=True,
|
||||
)
|
||||
self.session.add(status)
|
||||
await self.session.commit()
|
||||
|
||||
return CreateStatusResponse(ok=True, message="Статус успешно создан")
|
||||
|
||||
async def update_status(self, request: UpdateStatusRequest) -> UpdateStatusResponse:
|
||||
status = await self._get_status_by_id(request.status.id)
|
||||
if not status:
|
||||
return UpdateStatusResponse(ok=False, message=f"Статус с ID {request.status.id} не найден")
|
||||
|
||||
status.name = request.status.name
|
||||
await self.session.commit()
|
||||
|
||||
return UpdateStatusResponse(ok=True, message="Статус успешно изменен")
|
||||
|
||||
async def update_status_order(self, request: UpdateStatusOrderRequest) -> UpdateStatusOrderResponse:
|
||||
statuses = await self._get_statuses_for_board(request.board_id)
|
||||
status_idx = 0
|
||||
while status_idx < len(statuses) and statuses[status_idx].id != request.status_id:
|
||||
status_idx += 1
|
||||
if status_idx == len(statuses):
|
||||
return UpdateStatusOrderResponse(ok=False, message=f"Статус с ID {request.status_id} не найден")
|
||||
|
||||
status = statuses.pop(status_idx)
|
||||
statuses.insert(request.new_ordinal_number - 1, status)
|
||||
new_ordinal_number = 1
|
||||
for status in statuses:
|
||||
status.ordinal_number = new_ordinal_number
|
||||
status.is_finishing = False
|
||||
new_ordinal_number += 1
|
||||
|
||||
statuses[-1].is_finishing = True
|
||||
|
||||
await self.session.commit()
|
||||
return UpdateStatusOrderResponse(ok=True, message="Порядок статусов изменен")
|
||||
|
||||
async def _count_deals_in_progress(self, status_id: int) -> int:
|
||||
stmt = (
|
||||
select(func.count(Deal.id))
|
||||
.where(
|
||||
and_(
|
||||
Deal.current_status_id == status_id,
|
||||
Deal.is_deleted == False,
|
||||
Deal.is_completed == False,
|
||||
)
|
||||
)
|
||||
)
|
||||
return (await self.session.scalars(stmt)).first()
|
||||
|
||||
async def _count_deals(self, status_id: int) -> int:
|
||||
stmt = (
|
||||
select(func.count(Deal.id))
|
||||
.where(Deal.current_status_id == status_id)
|
||||
)
|
||||
return (await self.session.scalars(stmt)).first()
|
||||
|
||||
async def _set_finishing_flag_to_prev_status(self, status: DealStatus):
|
||||
statuses = await self._get_statuses_for_board(status.board_id)
|
||||
if len(statuses) < 2:
|
||||
return
|
||||
statuses[-2].is_finishing = True
|
||||
statuses[-1].is_finishing = False
|
||||
|
||||
async def delete_status(self, status_id: int) -> DeleteStatusResponse:
|
||||
status = await self._get_status_by_id(status_id)
|
||||
if not status:
|
||||
return DeleteStatusResponse(ok=False, message=f"Статус с ID {status_id} не найден")
|
||||
|
||||
count_deals_in_progress = await self._count_deals_in_progress(status_id)
|
||||
if count_deals_in_progress != 0:
|
||||
return DeleteStatusResponse(ok=False, message="Нельзя удалить статус с активными сделками")
|
||||
|
||||
if status.is_finishing:
|
||||
await self._set_finishing_flag_to_prev_status(status)
|
||||
|
||||
count_deals = await self._count_deals(status_id)
|
||||
if count_deals == 0:
|
||||
await self.session.delete(status)
|
||||
else:
|
||||
status.is_deleted = True
|
||||
await self.session.commit()
|
||||
|
||||
return DeleteStatusResponse(ok=True, message="Статус успешно удален")
|
||||
Reference in New Issue
Block a user