feat: deals viewer mode and links for viewers
This commit is contained in:
@@ -60,3 +60,7 @@ DEAL_STATUS_STR = [
|
|||||||
|
|
||||||
# 30 minutes in seconds
|
# 30 minutes in seconds
|
||||||
INVITE_CODE_EXPIRY = 30 * 60
|
INVITE_CODE_EXPIRY = 30 * 60
|
||||||
|
|
||||||
|
|
||||||
|
DEAL_EDITOR = 'deal_editor'
|
||||||
|
DEALS_VIEWER = 'deals_viewer'
|
||||||
|
|||||||
@@ -45,8 +45,9 @@ async def create_deal_bill(
|
|||||||
async def cancel_deal_billing(
|
async def cancel_deal_billing(
|
||||||
session: SessionDependency,
|
session: SessionDependency,
|
||||||
request: CancelCardBillRequest,
|
request: CancelCardBillRequest,
|
||||||
|
user: CurrentUserDependency,
|
||||||
):
|
):
|
||||||
return await BillingService(session).cancel_card_billing(request)
|
return await BillingService(session).cancel_card_billing(user, request)
|
||||||
|
|
||||||
|
|
||||||
@billing_router.get(
|
@billing_router.get(
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from models import User
|
|||||||
from parsers import DealParser
|
from parsers import DealParser
|
||||||
from schemas.barcode import GetCardProductsBarcodesPdfRequest, GetCardProductsBarcodesPdfResponse
|
from schemas.barcode import GetCardProductsBarcodesPdfRequest, GetCardProductsBarcodesPdfResponse
|
||||||
from schemas.card import *
|
from schemas.card import *
|
||||||
from services.auth import get_current_user, authorized_user, guest_user
|
from services.auth import get_current_user, authorized_user, guest_user, user_deals_viewer, user_deal_editor
|
||||||
from services.barcode import BarcodeService
|
from services.barcode import BarcodeService
|
||||||
from services.billing import BillingService
|
from services.billing import BillingService
|
||||||
from services.card import CardsService
|
from services.card import CardsService
|
||||||
@@ -82,14 +82,14 @@ async def change_status(
|
|||||||
'/summaries',
|
'/summaries',
|
||||||
response_model=CardSummaryResponse,
|
response_model=CardSummaryResponse,
|
||||||
operation_id='getCardSummaries',
|
operation_id='getCardSummaries',
|
||||||
dependencies=[Depends(authorized_user)]
|
|
||||||
)
|
)
|
||||||
async def get_summary(
|
async def get_summary(
|
||||||
session: Annotated[AsyncSession, Depends(get_session)],
|
session: Annotated[AsyncSession, Depends(get_session)],
|
||||||
request: GetCardSummariesRequest,
|
request: GetCardSummariesRequest,
|
||||||
pagination: PaginationDependency,
|
pagination: PaginationDependency,
|
||||||
|
user: CurrentUserDependency,
|
||||||
):
|
):
|
||||||
return await CardsService(session).get_summary(request, pagination)
|
return await CardsService(session).get_summary(request, pagination, user)
|
||||||
|
|
||||||
|
|
||||||
@card_router.post(
|
@card_router.post(
|
||||||
@@ -151,13 +151,13 @@ async def update_general_info(
|
|||||||
'/update-products-and-services-general-info',
|
'/update-products-and-services-general-info',
|
||||||
response_model=ProductsAndServicesGeneralInfoResponse,
|
response_model=ProductsAndServicesGeneralInfoResponse,
|
||||||
operation_id='update_products_and_services_general_info',
|
operation_id='update_products_and_services_general_info',
|
||||||
dependencies=[Depends(authorized_user)]
|
|
||||||
)
|
)
|
||||||
async def update_products_and_services_general_info(
|
async def update_products_and_services_general_info(
|
||||||
request: ProductsAndServicesGeneralInfoRequest,
|
request: ProductsAndServicesGeneralInfoRequest,
|
||||||
session: Annotated[AsyncSession, Depends(get_session)],
|
session: Annotated[AsyncSession, Depends(get_session)],
|
||||||
|
user: CurrentUserDependency,
|
||||||
):
|
):
|
||||||
return await CardsService(session).update_products_and_services_general_info(request)
|
return await CardsService(session).update_products_and_services_general_info(request, user)
|
||||||
|
|
||||||
|
|
||||||
@card_router.post(
|
@card_router.post(
|
||||||
@@ -185,6 +185,7 @@ async def update_card_client(
|
|||||||
):
|
):
|
||||||
return await CardsService(session).update_card_client(request)
|
return await CardsService(session).update_card_client(request)
|
||||||
|
|
||||||
|
|
||||||
@card_router.post(
|
@card_router.post(
|
||||||
'/add-kit',
|
'/add-kit',
|
||||||
response_model=CardAddKitResponse,
|
response_model=CardAddKitResponse,
|
||||||
@@ -206,9 +207,8 @@ async def add_kit_to_card(
|
|||||||
async def create_guest_url(
|
async def create_guest_url(
|
||||||
session: SessionDependency,
|
session: SessionDependency,
|
||||||
request: CardCreateGuestUrlRequest,
|
request: CardCreateGuestUrlRequest,
|
||||||
user: CurrentUserDependency
|
|
||||||
):
|
):
|
||||||
return CardsService(session).create_guest_url(user, request)
|
return await CardsService(session).create_guest_url(request)
|
||||||
|
|
||||||
|
|
||||||
@card_router.get(
|
@card_router.get(
|
||||||
@@ -267,6 +267,7 @@ async def recalculate_card_price(
|
|||||||
'/employee',
|
'/employee',
|
||||||
response_model=ManageEmployeeResponse,
|
response_model=ManageEmployeeResponse,
|
||||||
operation_id='manage_employee',
|
operation_id='manage_employee',
|
||||||
|
dependencies=[Depends(user_deal_editor)],
|
||||||
)
|
)
|
||||||
async def manage_employee(
|
async def manage_employee(
|
||||||
session: SessionDependency,
|
session: SessionDependency,
|
||||||
@@ -279,6 +280,7 @@ async def manage_employee(
|
|||||||
'/employee/available/{card_id}',
|
'/employee/available/{card_id}',
|
||||||
response_model=GetAvailableEmployeesToAssignResponse,
|
response_model=GetAvailableEmployeesToAssignResponse,
|
||||||
operation_id='get_available_employees_to_assign',
|
operation_id='get_available_employees_to_assign',
|
||||||
|
dependencies=[Depends(user_deal_editor)],
|
||||||
)
|
)
|
||||||
async def get_available_employees_to_assign(
|
async def get_available_employees_to_assign(
|
||||||
session: Annotated[AsyncSession, Depends(get_session)],
|
session: Annotated[AsyncSession, Depends(get_session)],
|
||||||
@@ -291,6 +293,7 @@ async def get_available_employees_to_assign(
|
|||||||
'/prefill/excel/parse',
|
'/prefill/excel/parse',
|
||||||
response_model=ParseCardsExcelResponse,
|
response_model=ParseCardsExcelResponse,
|
||||||
operation_id='parse_deals_excel',
|
operation_id='parse_deals_excel',
|
||||||
|
dependencies=[Depends(authorized_user)]
|
||||||
)
|
)
|
||||||
async def parse_deals_excel(
|
async def parse_deals_excel(
|
||||||
session: Annotated[AsyncSession, Depends(get_session)],
|
session: Annotated[AsyncSession, Depends(get_session)],
|
||||||
@@ -304,6 +307,7 @@ async def parse_deals_excel(
|
|||||||
'/prefill/excel/create',
|
'/prefill/excel/create',
|
||||||
response_model=CreateCardsFromExcelResponse,
|
response_model=CreateCardsFromExcelResponse,
|
||||||
operation_id='create_deals_excel',
|
operation_id='create_deals_excel',
|
||||||
|
dependencies=[Depends(authorized_user)],
|
||||||
)
|
)
|
||||||
async def create_deals_from_excel(
|
async def create_deals_from_excel(
|
||||||
session: Annotated[AsyncSession, Depends(get_session)],
|
session: Annotated[AsyncSession, Depends(get_session)],
|
||||||
@@ -312,6 +316,7 @@ async def create_deals_from_excel(
|
|||||||
):
|
):
|
||||||
return await CardsService(session).create_cards_from_excel(request, user)
|
return await CardsService(session).create_cards_from_excel(request, user)
|
||||||
|
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
# region Card services
|
# region Card services
|
||||||
@@ -519,5 +524,4 @@ async def get_card_products_barcodes_pdf(
|
|||||||
mime_type='application/pdf'
|
mime_type='application/pdf'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from typing import Annotated
|
|||||||
from fastapi import APIRouter, Depends, UploadFile
|
from fastapi import APIRouter, Depends, UploadFile
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from backend.dependecies import CurrentUserDependency
|
||||||
from backend.session import get_session
|
from backend.session import get_session
|
||||||
from models import User
|
from models import User
|
||||||
from schemas.chat import *
|
from schemas.chat import *
|
||||||
@@ -111,8 +112,9 @@ async def get_client_chats_list(
|
|||||||
async def create_chat(
|
async def create_chat(
|
||||||
session: Annotated[AsyncSession, Depends(get_session)],
|
session: Annotated[AsyncSession, Depends(get_session)],
|
||||||
request: CreateChatRequest,
|
request: CreateChatRequest,
|
||||||
|
user: CurrentUserDependency,
|
||||||
):
|
):
|
||||||
return await ChatService(session).create_chat(request)
|
return await ChatService(session).create_chat(request, user)
|
||||||
|
|
||||||
|
|
||||||
@chat_router.post(
|
@chat_router.post(
|
||||||
|
|||||||
@@ -98,3 +98,15 @@ async def delete_client(
|
|||||||
session: Annotated[AsyncSession, Depends(get_session)]
|
session: Annotated[AsyncSession, Depends(get_session)]
|
||||||
):
|
):
|
||||||
return await ClientService(session).delete(request)
|
return await ClientService(session).delete(request)
|
||||||
|
|
||||||
|
|
||||||
|
@client_router.post(
|
||||||
|
'/link/{client_id}',
|
||||||
|
operation_id='client_link',
|
||||||
|
response_model=CreateGuestUrlResponse,
|
||||||
|
)
|
||||||
|
async def client_link(
|
||||||
|
session: Annotated[AsyncSession, Depends(get_session)],
|
||||||
|
client_id: int,
|
||||||
|
):
|
||||||
|
return await ClientService(session).create_guest_url(client_id)
|
||||||
|
|||||||
@@ -8,17 +8,16 @@ from services.marketplace import MarketplaceService
|
|||||||
marketplace_router = APIRouter(
|
marketplace_router = APIRouter(
|
||||||
prefix="/marketplace",
|
prefix="/marketplace",
|
||||||
tags=["marketplace"],
|
tags=["marketplace"],
|
||||||
dependencies=[Depends(authorized_user)]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@marketplace_router.get(
|
@marketplace_router.get(
|
||||||
'/base/get-all',
|
'/base/get-all',
|
||||||
operation_id='get_all_base_marketplaces',
|
operation_id='get_all_base_marketplaces',
|
||||||
response_model=GetAllBaseMarketplacesResponse
|
response_model=GetAllBaseMarketplacesResponse,
|
||||||
)
|
)
|
||||||
async def get_all(
|
async def get_all(
|
||||||
session: SessionDependency
|
session: SessionDependency,
|
||||||
):
|
):
|
||||||
return await MarketplaceService(session).get_all_base_marketplaces()
|
return await MarketplaceService(session).get_all_base_marketplaces()
|
||||||
|
|
||||||
@@ -26,7 +25,8 @@ async def get_all(
|
|||||||
@marketplace_router.post(
|
@marketplace_router.post(
|
||||||
'/get',
|
'/get',
|
||||||
operation_id='get_client_marketplaces',
|
operation_id='get_client_marketplaces',
|
||||||
response_model=GetClientMarketplacesResponse
|
response_model=GetClientMarketplacesResponse,
|
||||||
|
dependencies=[Depends(authorized_user)]
|
||||||
)
|
)
|
||||||
async def get(
|
async def get(
|
||||||
session: SessionDependency,
|
session: SessionDependency,
|
||||||
@@ -38,7 +38,8 @@ async def get(
|
|||||||
@marketplace_router.post(
|
@marketplace_router.post(
|
||||||
'/create',
|
'/create',
|
||||||
operation_id='create_marketplace',
|
operation_id='create_marketplace',
|
||||||
response_model=CreateMarketplaceResponse
|
response_model=CreateMarketplaceResponse,
|
||||||
|
dependencies=[Depends(authorized_user)],
|
||||||
)
|
)
|
||||||
async def create(
|
async def create(
|
||||||
session: SessionDependency,
|
session: SessionDependency,
|
||||||
@@ -50,7 +51,8 @@ async def create(
|
|||||||
@marketplace_router.post(
|
@marketplace_router.post(
|
||||||
'/delete',
|
'/delete',
|
||||||
operation_id='delete_marketplace',
|
operation_id='delete_marketplace',
|
||||||
response_model=DeleteMarketplaceResponse
|
response_model=DeleteMarketplaceResponse,
|
||||||
|
dependencies=[Depends(authorized_user)],
|
||||||
)
|
)
|
||||||
async def delete(
|
async def delete(
|
||||||
session: SessionDependency,
|
session: SessionDependency,
|
||||||
@@ -62,7 +64,8 @@ async def delete(
|
|||||||
@marketplace_router.post(
|
@marketplace_router.post(
|
||||||
'/update',
|
'/update',
|
||||||
operation_id='update_marketplace',
|
operation_id='update_marketplace',
|
||||||
response_model=UpdateMarketplaceResponse
|
response_model=UpdateMarketplaceResponse,
|
||||||
|
dependencies=[Depends(authorized_user)],
|
||||||
)
|
)
|
||||||
async def update(
|
async def update(
|
||||||
session: SessionDependency,
|
session: SessionDependency,
|
||||||
@@ -70,10 +73,12 @@ async def update(
|
|||||||
):
|
):
|
||||||
return await MarketplaceService(session).update_marketplace(request)
|
return await MarketplaceService(session).update_marketplace(request)
|
||||||
|
|
||||||
|
|
||||||
@marketplace_router.post(
|
@marketplace_router.post(
|
||||||
'/yandex-market/get-campaigns',
|
'/yandex-market/get-campaigns',
|
||||||
operation_id='get_yandex_market_campaigns',
|
operation_id='get_yandex_market_campaigns',
|
||||||
response_model=GetYandexMarketCampaignsResponse
|
response_model=GetYandexMarketCampaignsResponse,
|
||||||
|
dependencies=[Depends(authorized_user)],
|
||||||
)
|
)
|
||||||
async def get_yandex_market_campaigns(
|
async def get_yandex_market_campaigns(
|
||||||
session: SessionDependency,
|
session: SessionDependency,
|
||||||
|
|||||||
@@ -31,8 +31,9 @@ product_router = APIRouter(
|
|||||||
async def create_product(
|
async def create_product(
|
||||||
request: ProductCreateRequest,
|
request: ProductCreateRequest,
|
||||||
session: Annotated[AsyncSession, Depends(get_session)],
|
session: Annotated[AsyncSession, Depends(get_session)],
|
||||||
|
user: CurrentUserDependency,
|
||||||
):
|
):
|
||||||
return await ProductService(session).create(request)
|
return await ProductService(session).create(request, user)
|
||||||
|
|
||||||
|
|
||||||
@product_router.post(
|
@product_router.post(
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|||||||
|
|
||||||
from backend.session import get_session
|
from backend.session import get_session
|
||||||
from schemas.project import *
|
from schemas.project import *
|
||||||
from services.auth import guest_user, authorized_user
|
from services.auth import guest_user, authorized_user, user_deals_viewer
|
||||||
from services.project import ProjectService
|
from services.project import ProjectService
|
||||||
|
|
||||||
project_router = APIRouter(
|
project_router = APIRouter(
|
||||||
@@ -18,6 +18,7 @@ project_router = APIRouter(
|
|||||||
"/",
|
"/",
|
||||||
response_model=GetProjectsResponse,
|
response_model=GetProjectsResponse,
|
||||||
operation_id="get_projects",
|
operation_id="get_projects",
|
||||||
|
dependencies=[Depends(user_deals_viewer)],
|
||||||
)
|
)
|
||||||
async def get_projects(
|
async def get_projects(
|
||||||
session: Annotated[AsyncSession, Depends(get_session)],
|
session: Annotated[AsyncSession, Depends(get_session)],
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from typing import Optional
|
from typing import Optional, TypedDict, Union
|
||||||
|
|
||||||
|
from models import User
|
||||||
from schemas.base import BaseSchema, CustomModelSnake
|
from schemas.base import BaseSchema, CustomModelSnake
|
||||||
|
|
||||||
|
|
||||||
@@ -14,3 +15,16 @@ class AuthLoginRequest(CustomModelSnake):
|
|||||||
|
|
||||||
class AuthLoginResponse(BaseSchema):
|
class AuthLoginResponse(BaseSchema):
|
||||||
access_token: str
|
access_token: str
|
||||||
|
|
||||||
|
|
||||||
|
class UserDealEditor(TypedDict):
|
||||||
|
deal_id: int
|
||||||
|
sub: str
|
||||||
|
|
||||||
|
|
||||||
|
class UserViewer(TypedDict):
|
||||||
|
client_id: int
|
||||||
|
sub: str
|
||||||
|
|
||||||
|
|
||||||
|
UserUnion = Union[User, UserViewer, UserDealEditor]
|
||||||
|
|||||||
@@ -87,4 +87,9 @@ class ClientUpdateResponse(OkMessageSchema):
|
|||||||
|
|
||||||
class ClientDeleteResponse(OkMessageSchema):
|
class ClientDeleteResponse(OkMessageSchema):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CreateGuestUrlResponse(OkMessageSchema):
|
||||||
|
url: str
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from starlette import status
|
|||||||
import backend.config
|
import backend.config
|
||||||
import constants
|
import constants
|
||||||
from backend.session import get_session
|
from backend.session import get_session
|
||||||
|
from constants import DEALS_VIEWER, DEAL_EDITOR
|
||||||
from enums.user import UserRole
|
from enums.user import UserRole
|
||||||
from models import User, InviteCode
|
from models import User, InviteCode
|
||||||
from schemas.auth import *
|
from schemas.auth import *
|
||||||
@@ -23,7 +24,7 @@ algorithm = 'HS256'
|
|||||||
async def get_current_user(
|
async def get_current_user(
|
||||||
session: Annotated[AsyncSession, Depends(get_session)],
|
session: Annotated[AsyncSession, Depends(get_session)],
|
||||||
token: Annotated[HTTPAuthorizationCredentials, Depends(oauth2_schema)]
|
token: Annotated[HTTPAuthorizationCredentials, Depends(oauth2_schema)]
|
||||||
) -> Union[User, None, dict]:
|
) -> Optional[UserUnion]:
|
||||||
if not token.credentials:
|
if not token.credentials:
|
||||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='Invalid token')
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='Invalid token')
|
||||||
try:
|
try:
|
||||||
@@ -31,11 +32,11 @@ async def get_current_user(
|
|||||||
user_id = payload.get('sub')
|
user_id = payload.get('sub')
|
||||||
if not user_id:
|
if not user_id:
|
||||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail='Invalid credentials')
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail='Invalid credentials')
|
||||||
if user_id == 'guest':
|
if user_id == DEAL_EDITOR or user_id == DEALS_VIEWER:
|
||||||
return payload
|
return payload
|
||||||
user_id = int(user_id)
|
user_id = int(user_id)
|
||||||
|
|
||||||
user = await session.get(User, user_id)
|
user: Optional[User] = await session.get(User, user_id)
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='Invalid credentials')
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='Invalid credentials')
|
||||||
if user.is_deleted or user.is_blocked:
|
if user.is_deleted or user.is_blocked:
|
||||||
@@ -53,12 +54,24 @@ async def authorized_user(
|
|||||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='Invalid token')
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='Invalid token')
|
||||||
|
|
||||||
|
|
||||||
async def guest_user(user: Annotated[User, Depends(get_current_user)]):
|
async def guest_user(user: Annotated[UserUnion, Depends(get_current_user)]):
|
||||||
if (type(user) is User) or (type(user) is dict):
|
if (type(user) is User) or (type(user) is dict):
|
||||||
return user
|
return user
|
||||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='Invalid token')
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='Invalid token')
|
||||||
|
|
||||||
|
|
||||||
|
async def user_deals_viewer(user: Annotated[UserUnion, Depends(get_current_user)]):
|
||||||
|
if (type(user) is User) or (type(user) is dict and user['sub'] == DEALS_VIEWER):
|
||||||
|
return user
|
||||||
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='Invalid token')
|
||||||
|
|
||||||
|
|
||||||
|
async def user_deal_editor(user: Annotated[UserUnion, Depends(get_current_user)]):
|
||||||
|
if (type(user) is User) or (type(user) is dict and user['sub'] == DEAL_EDITOR):
|
||||||
|
return user
|
||||||
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='Invalid token')
|
||||||
|
|
||||||
|
|
||||||
class AuthService(BaseService):
|
class AuthService(BaseService):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _generate_jwt_token(payload: dict) -> str:
|
def _generate_jwt_token(payload: dict) -> str:
|
||||||
@@ -101,9 +114,17 @@ class AuthService(BaseService):
|
|||||||
return AuthLoginResponse(access_token=access_token)
|
return AuthLoginResponse(access_token=access_token)
|
||||||
|
|
||||||
def create_deal_guest_token(self, deal_id: int):
|
def create_deal_guest_token(self, deal_id: int):
|
||||||
payload = {
|
payload: UserDealEditor = {
|
||||||
'sub': 'guest',
|
'sub': constants.DEAL_EDITOR,
|
||||||
'deal_id': deal_id
|
'deal_id': deal_id
|
||||||
}
|
}
|
||||||
|
|
||||||
return self._generate_jwt_token(payload)
|
return self._generate_jwt_token(payload)
|
||||||
|
|
||||||
|
def create_client_guest_token(self, client_id: int):
|
||||||
|
payload: UserViewer = {
|
||||||
|
'sub': constants.DEALS_VIEWER,
|
||||||
|
'client_id': client_id
|
||||||
|
}
|
||||||
|
|
||||||
|
return self._generate_jwt_token(payload)
|
||||||
|
|||||||
@@ -17,9 +17,11 @@ from external.billing import BillingClient, CreateBillingRequestValue, CreateBil
|
|||||||
ProductBillingDocumentPdf, ServiceBillingDocumentPdf
|
ProductBillingDocumentPdf, ServiceBillingDocumentPdf
|
||||||
from models import CardBillRequest, Card, CardProduct, CardService as CardServiceModel, CardGroup, \
|
from models import CardBillRequest, Card, CardProduct, CardService as CardServiceModel, CardGroup, \
|
||||||
GroupBillRequest
|
GroupBillRequest
|
||||||
|
from schemas.auth import UserUnion
|
||||||
from schemas.billing import *
|
from schemas.billing import *
|
||||||
from services.base import BaseService
|
from services.base import BaseService
|
||||||
from services.card import CardsService
|
from services.card import CardsService
|
||||||
|
from utils.auth import verify_user_deal_editor
|
||||||
from utils.list_utils import to_locale_number
|
from utils.list_utils import to_locale_number
|
||||||
|
|
||||||
|
|
||||||
@@ -157,6 +159,8 @@ class BillingService(BaseService):
|
|||||||
|
|
||||||
async def create_card_billing(self, user, request: CreateCardBillRequest) -> CreateCardBillResponse:
|
async def create_card_billing(self, user, request: CreateCardBillRequest) -> CreateCardBillResponse:
|
||||||
try:
|
try:
|
||||||
|
verify_user_deal_editor(user)
|
||||||
|
|
||||||
card_service = CardsService(self.session)
|
card_service = CardsService(self.session)
|
||||||
billing_client = BillingClient(backend.config.BILLING_API_KEY)
|
billing_client = BillingClient(backend.config.BILLING_API_KEY)
|
||||||
|
|
||||||
@@ -261,8 +265,10 @@ class BillingService(BaseService):
|
|||||||
|
|
||||||
await self.session.delete(bill)
|
await self.session.delete(bill)
|
||||||
|
|
||||||
async def cancel_card_billing(self, request: CancelCardBillRequest) -> CancelCardBillResponse:
|
async def cancel_card_billing(self, user: UserUnion, request: CancelCardBillRequest) -> CancelCardBillResponse:
|
||||||
try:
|
try:
|
||||||
|
verify_user_deal_editor(user)
|
||||||
|
|
||||||
card = await self._get_card_by_id(request.card_id)
|
card = await self._get_card_by_id(request.card_id)
|
||||||
if not card:
|
if not card:
|
||||||
return CancelCardBillResponse(ok=False, message='Сделка не найдена')
|
return CancelCardBillResponse(ok=False, message='Сделка не найдена')
|
||||||
|
|||||||
107
services/card.py
107
services/card.py
@@ -2,14 +2,15 @@ from collections import defaultdict
|
|||||||
|
|
||||||
import lexorank
|
import lexorank
|
||||||
import math
|
import math
|
||||||
|
import starlette.status
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
from sqlalchemy import select, func, update, delete, insert, and_, Select
|
from sqlalchemy import select, func, update, delete, insert, and_, Select
|
||||||
from sqlalchemy.orm import joinedload, selectinload, noload
|
from sqlalchemy.orm import joinedload, selectinload, noload
|
||||||
from starlette import status
|
|
||||||
|
|
||||||
from card_attributes import CardAttributesCommandHandler
|
from card_attributes import CardAttributesCommandHandler
|
||||||
from card_attributes.exceptions import CardAttributeException
|
from card_attributes.exceptions import CardAttributeException
|
||||||
from models import *
|
from models import *
|
||||||
|
from schemas.auth import UserUnion
|
||||||
from schemas.base import PaginationSchema
|
from schemas.base import PaginationSchema
|
||||||
from schemas.card import *
|
from schemas.card import *
|
||||||
from schemas.client import ClientDetailsSchema
|
from schemas.client import ClientDetailsSchema
|
||||||
@@ -20,20 +21,13 @@ from services.card_tag import CardTagService
|
|||||||
from services.client import ClientService
|
from services.client import ClientService
|
||||||
from services.service import ServiceService
|
from services.service import ServiceService
|
||||||
from services.shipping_warehouse import ShippingWarehouseService
|
from services.shipping_warehouse import ShippingWarehouseService
|
||||||
|
from utils.auth import verify_user_deal_editor, verify_user_viewer
|
||||||
|
|
||||||
|
|
||||||
class CardsService(BaseService):
|
class CardsService(BaseService):
|
||||||
|
|
||||||
# region Card
|
# region Card
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def grant_access(user: Union[User, dict], card_id: int):
|
|
||||||
if type(user) is User:
|
|
||||||
return
|
|
||||||
user_card_id = user['deal_id']
|
|
||||||
if int(user_card_id) != int(card_id):
|
|
||||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='Invalid token')
|
|
||||||
|
|
||||||
async def _get_card_by_id(self, card_id) -> Union[Card, None]:
|
async def _get_card_by_id(self, card_id) -> Union[Card, None]:
|
||||||
return await self.session.get(Card, card_id)
|
return await self.session.get(Card, card_id)
|
||||||
|
|
||||||
@@ -235,7 +229,12 @@ class CardsService(BaseService):
|
|||||||
total_items=total_items,
|
total_items=total_items,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def get_summary(self, request: GetCardSummariesRequest, pagination: PaginationSchema) -> CardSummaryResponse:
|
async def get_summary(
|
||||||
|
self,
|
||||||
|
request: GetCardSummariesRequest,
|
||||||
|
pagination: PaginationSchema,
|
||||||
|
user: UserUnion,
|
||||||
|
) -> CardSummaryResponse:
|
||||||
price_subquery = self._get_price_subquery()
|
price_subquery = self._get_price_subquery()
|
||||||
products_quantity_subquery = self._get_products_quantity_subquery()
|
products_quantity_subquery = self._get_products_quantity_subquery()
|
||||||
q = (
|
q = (
|
||||||
@@ -268,6 +267,9 @@ class CardsService(BaseService):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if isinstance(user, dict) and "client_id" in user:
|
||||||
|
q = q.where(Card.client_id == user["client_id"])
|
||||||
|
|
||||||
q = self._apply_summary_filters(q, request)
|
q = self._apply_summary_filters(q, request)
|
||||||
|
|
||||||
pagination_info = await self._summaries_pagination_info(q, pagination)
|
pagination_info = await self._summaries_pagination_info(q, pagination)
|
||||||
@@ -346,10 +348,9 @@ class CardsService(BaseService):
|
|||||||
result.append(CardSchema.model_validate(card))
|
result.append(CardSchema.model_validate(card))
|
||||||
return CardGetAllResponse(cards=result)
|
return CardGetAllResponse(cards=result)
|
||||||
|
|
||||||
async def get_by_id(self, user: Union[User, dict], card_id: int, return_raw=False) -> Union[
|
async def get_by_id(self, user: UserUnion, card_id: int, return_raw=False) -> Union[
|
||||||
CardSchema, Card]:
|
CardSchema, Card
|
||||||
self.grant_access(user, card_id)
|
]:
|
||||||
|
|
||||||
card: Optional[Card] = await self.session.scalar(
|
card: Optional[Card] = await self.session.scalar(
|
||||||
select(Card)
|
select(Card)
|
||||||
.options(
|
.options(
|
||||||
@@ -393,11 +394,17 @@ class CardsService(BaseService):
|
|||||||
)
|
)
|
||||||
.where(Card.id == card_id)
|
.where(Card.id == card_id)
|
||||||
)
|
)
|
||||||
if return_raw:
|
|
||||||
return card
|
|
||||||
if not card:
|
if not card:
|
||||||
raise HTTPException(status_code=404, detail="Карточка не найдена")
|
raise HTTPException(status_code=404, detail="Карточка не найдена")
|
||||||
|
|
||||||
|
if not verify_user_deal_editor(user, card_id, False) and \
|
||||||
|
not verify_user_viewer(user, card.client_id, False):
|
||||||
|
raise HTTPException(status_code=starlette.status.HTTP_403_FORBIDDEN, detail='Forbidden')
|
||||||
|
|
||||||
|
if return_raw:
|
||||||
|
return card
|
||||||
|
|
||||||
return CardSchema.model_validate(card)
|
return CardSchema.model_validate(card)
|
||||||
|
|
||||||
async def update_general_info(
|
async def update_general_info(
|
||||||
@@ -450,12 +457,15 @@ class CardsService(BaseService):
|
|||||||
await self.session.rollback()
|
await self.session.rollback()
|
||||||
return CardUpdateGeneralInfoResponse(ok=False, message=str(e))
|
return CardUpdateGeneralInfoResponse(ok=False, message=str(e))
|
||||||
|
|
||||||
async def update_products_and_services_general_info(self, request: ProductsAndServicesGeneralInfoRequest) -> (
|
async def update_products_and_services_general_info(self, request: ProductsAndServicesGeneralInfoRequest,
|
||||||
|
user: UserUnion) -> (
|
||||||
ProductsAndServicesGeneralInfoResponse
|
ProductsAndServicesGeneralInfoResponse
|
||||||
):
|
):
|
||||||
|
verify_user_deal_editor(user, request.card_id)
|
||||||
|
|
||||||
card: Optional[Card] = await self.session.get(Card, request.card_id)
|
card: Optional[Card] = await self.session.get(Card, request.card_id)
|
||||||
if not card:
|
if not card:
|
||||||
return ProductsAndServicesGeneralInfoRequest(ok=False, message='Карточка не найдена')
|
return ProductsAndServicesGeneralInfoResponse(ok=False, message='Карточка не найдена')
|
||||||
|
|
||||||
# Updating shipping warehouse
|
# Updating shipping warehouse
|
||||||
shipping_warehouse_service = ShippingWarehouseService(self.session)
|
shipping_warehouse_service = ShippingWarehouseService(self.session)
|
||||||
@@ -464,8 +474,10 @@ class CardsService(BaseService):
|
|||||||
shipping_warehouse = await shipping_warehouse_service.create_by_name(request.data.shipping_warehouse)
|
shipping_warehouse = await shipping_warehouse_service.create_by_name(request.data.shipping_warehouse)
|
||||||
|
|
||||||
if card.group:
|
if card.group:
|
||||||
for card in card.group.cards:
|
stmt_group = select(CardGroup).where(CardGroup.id == card.group.id).options(selectinload(CardGroup.cards))
|
||||||
card.is_services_profit_accounted = request.data.is_services_profit_accounted
|
group = (await self.session.scalars(stmt_group)).one_or_none()
|
||||||
|
for c in group.cards:
|
||||||
|
c.is_services_profit_accounted = request.data.is_services_profit_accounted
|
||||||
else:
|
else:
|
||||||
card.is_services_profit_accounted = request.data.is_services_profit_accounted
|
card.is_services_profit_accounted = request.data.is_services_profit_accounted
|
||||||
|
|
||||||
@@ -579,7 +591,7 @@ class CardsService(BaseService):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return CardAddKitResponse(ok=False, message=str(e))
|
return CardAddKitResponse(ok=False, message=str(e))
|
||||||
|
|
||||||
def create_guest_url(self, user: User, request: CardCreateGuestUrlRequest) -> CardCreateGuestUrlResponse:
|
async def create_guest_url(self, request: CardCreateGuestUrlRequest) -> CardCreateGuestUrlResponse:
|
||||||
access_token = AuthService(self.session).create_deal_guest_token(request.card_id)
|
access_token = AuthService(self.session).create_deal_guest_token(request.card_id)
|
||||||
url = f"deals/{request.card_id}?accessToken={access_token}"
|
url = f"deals/{request.card_id}?accessToken={access_token}"
|
||||||
return CardCreateGuestUrlResponse(ok=True, message='Ссылка успешно создана!', url=url)
|
return CardCreateGuestUrlResponse(ok=True, message='Ссылка успешно создана!', url=url)
|
||||||
@@ -668,11 +680,11 @@ class CardsService(BaseService):
|
|||||||
|
|
||||||
async def update_service_quantity(
|
async def update_service_quantity(
|
||||||
self,
|
self,
|
||||||
user: Union[User, dict],
|
user: UserUnion,
|
||||||
request: CardUpdateServiceQuantityRequest
|
request: CardUpdateServiceQuantityRequest
|
||||||
) -> CardUpdateServiceQuantityResponse:
|
) -> CardUpdateServiceQuantityResponse:
|
||||||
try:
|
try:
|
||||||
self.grant_access(user, request.card_id)
|
verify_user_deal_editor(user, request.card_id)
|
||||||
card_service = await self.session.scalar(
|
card_service = await self.session.scalar(
|
||||||
select(CardService)
|
select(CardService)
|
||||||
.where(CardService.card_id == request.card_id,
|
.where(CardService.card_id == request.card_id,
|
||||||
@@ -689,11 +701,11 @@ class CardsService(BaseService):
|
|||||||
|
|
||||||
async def add_service(
|
async def add_service(
|
||||||
self,
|
self,
|
||||||
user: Union[User, dict],
|
user: UserUnion,
|
||||||
request: CardAddServiceRequest
|
request: CardAddServiceRequest
|
||||||
) -> CardAddServiceResponse:
|
) -> CardAddServiceResponse:
|
||||||
try:
|
try:
|
||||||
self.grant_access(user, request.card_id)
|
verify_user_deal_editor(user, request.card_id)
|
||||||
card = await self.session.scalar(select(Card).where(Card.id == request.card_id))
|
card = await self.session.scalar(select(Card).where(Card.id == request.card_id))
|
||||||
if not card:
|
if not card:
|
||||||
raise HTTPException(status_code=404, detail="Карточка не найдена")
|
raise HTTPException(status_code=404, detail="Карточка не найдена")
|
||||||
@@ -723,11 +735,12 @@ class CardsService(BaseService):
|
|||||||
|
|
||||||
async def delete_service(
|
async def delete_service(
|
||||||
self,
|
self,
|
||||||
user: Union[User, dict],
|
user: UserUnion,
|
||||||
request: CardDeleteServiceRequest
|
request: CardDeleteServiceRequest
|
||||||
) -> CardDeleteServiceResponse:
|
) -> CardDeleteServiceResponse:
|
||||||
try:
|
try:
|
||||||
self.grant_access(user, request.card_id)
|
verify_user_deal_editor(user, request.card_id)
|
||||||
|
|
||||||
card_service = await self.session.scalar(
|
card_service = await self.session.scalar(
|
||||||
select(CardService)
|
select(CardService)
|
||||||
.where(CardService.card_id == request.card_id,
|
.where(CardService.card_id == request.card_id,
|
||||||
@@ -744,11 +757,12 @@ class CardsService(BaseService):
|
|||||||
|
|
||||||
async def delete_services(
|
async def delete_services(
|
||||||
self,
|
self,
|
||||||
user: Union[User, dict],
|
user: UserUnion,
|
||||||
request: CardDeleteServicesRequest
|
request: CardDeleteServicesRequest
|
||||||
) -> CardDeleteServicesResponse:
|
) -> CardDeleteServicesResponse:
|
||||||
try:
|
try:
|
||||||
self.grant_access(user, request)
|
verify_user_deal_editor(user, request.card_id)
|
||||||
|
|
||||||
card_services = await self.session.scalars(
|
card_services = await self.session.scalars(
|
||||||
select(CardService)
|
select(CardService)
|
||||||
.where(CardService.card_id == request.card_id,
|
.where(CardService.card_id == request.card_id,
|
||||||
@@ -764,11 +778,11 @@ class CardsService(BaseService):
|
|||||||
|
|
||||||
async def update_service(
|
async def update_service(
|
||||||
self,
|
self,
|
||||||
user: Union[User, dict],
|
user: UserUnion,
|
||||||
request: CardUpdateServiceRequest
|
request: CardUpdateServiceRequest
|
||||||
) -> CardUpdateServiceResponse:
|
) -> CardUpdateServiceResponse:
|
||||||
try:
|
try:
|
||||||
self.grant_access(user, request.card_id)
|
verify_user_deal_editor(user, request.card_id)
|
||||||
card_service = await self.session.scalar(
|
card_service = await self.session.scalar(
|
||||||
select(CardService)
|
select(CardService)
|
||||||
.where(CardService.card_id == request.card_id,
|
.where(CardService.card_id == request.card_id,
|
||||||
@@ -823,11 +837,11 @@ class CardsService(BaseService):
|
|||||||
|
|
||||||
async def copy_services(
|
async def copy_services(
|
||||||
self,
|
self,
|
||||||
user: Union[User, dict],
|
user: UserUnion,
|
||||||
request: CardServicesCopyRequest
|
request: CardServicesCopyRequest
|
||||||
) -> CardServicesCopyResponse:
|
) -> CardServicesCopyResponse:
|
||||||
try:
|
try:
|
||||||
self.grant_access(user, request.card_id)
|
verify_user_deal_editor(user, request.card_id)
|
||||||
source_services_stmt = (
|
source_services_stmt = (
|
||||||
select(
|
select(
|
||||||
CardProductService
|
CardProductService
|
||||||
@@ -911,11 +925,11 @@ class CardsService(BaseService):
|
|||||||
# region Card products
|
# region Card products
|
||||||
async def update_product_quantity(
|
async def update_product_quantity(
|
||||||
self,
|
self,
|
||||||
user: Union[User, dict],
|
user: UserUnion,
|
||||||
request: CardUpdateProductQuantityRequest
|
request: CardUpdateProductQuantityRequest
|
||||||
) -> CardUpdateProductQuantityResponse:
|
) -> CardUpdateProductQuantityResponse:
|
||||||
try:
|
try:
|
||||||
self.grant_access(user, request.card_id)
|
verify_user_deal_editor(user, request.card_id)
|
||||||
# check if there is no card or no product with different exceptions
|
# check if there is no card or no product with different exceptions
|
||||||
card_product = await self.session.scalar(
|
card_product = await self.session.scalar(
|
||||||
select(CardProduct)
|
select(CardProduct)
|
||||||
@@ -933,12 +947,11 @@ class CardsService(BaseService):
|
|||||||
|
|
||||||
async def add_product(
|
async def add_product(
|
||||||
self,
|
self,
|
||||||
user: Union[User, dict],
|
user: UserUnion,
|
||||||
|
|
||||||
request: CardAddProductRequest
|
request: CardAddProductRequest
|
||||||
) -> CardAddProductResponse:
|
) -> CardAddProductResponse:
|
||||||
try:
|
try:
|
||||||
self.grant_access(user, request.card_id)
|
verify_user_deal_editor(user, request.card_id)
|
||||||
|
|
||||||
card = await self.session.scalar(select(Card).where(Card.id == request.card_id))
|
card = await self.session.scalar(select(Card).where(Card.id == request.card_id))
|
||||||
if not card:
|
if not card:
|
||||||
@@ -978,11 +991,12 @@ class CardsService(BaseService):
|
|||||||
|
|
||||||
async def delete_product(
|
async def delete_product(
|
||||||
self,
|
self,
|
||||||
user: Union[User, dict],
|
user: UserUnion,
|
||||||
request: CardDeleteProductRequest
|
request: CardDeleteProductRequest
|
||||||
) -> CardDeleteProductResponse:
|
) -> CardDeleteProductResponse:
|
||||||
try:
|
try:
|
||||||
self.grant_access(user, request.card_id)
|
verify_user_deal_editor(user, request.card_id)
|
||||||
|
|
||||||
card_product = await self.session.scalar(
|
card_product = await self.session.scalar(
|
||||||
select(CardProduct)
|
select(CardProduct)
|
||||||
.where(CardProduct.card_id == request.card_id,
|
.where(CardProduct.card_id == request.card_id,
|
||||||
@@ -999,11 +1013,11 @@ class CardsService(BaseService):
|
|||||||
|
|
||||||
async def delete_products(
|
async def delete_products(
|
||||||
self,
|
self,
|
||||||
user: Union[User, dict],
|
user: UserUnion,
|
||||||
request: CardDeleteProductsRequest
|
request: CardDeleteProductsRequest
|
||||||
) -> CardDeleteProductsResponse:
|
) -> CardDeleteProductsResponse:
|
||||||
try:
|
try:
|
||||||
self.grant_access(user, request.card_id)
|
verify_user_deal_editor(user, request.card_id)
|
||||||
card_products = await self.session.scalars(
|
card_products = await self.session.scalars(
|
||||||
select(CardProduct)
|
select(CardProduct)
|
||||||
.where(CardProduct.card_id == request.card_id,
|
.where(CardProduct.card_id == request.card_id,
|
||||||
@@ -1019,11 +1033,11 @@ class CardsService(BaseService):
|
|||||||
|
|
||||||
async def update_product(
|
async def update_product(
|
||||||
self,
|
self,
|
||||||
user: Union[User, dict],
|
user: UserUnion,
|
||||||
request: CardUpdateProductRequest
|
request: CardUpdateProductRequest
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
self.grant_access(user, request.card_id)
|
verify_user_deal_editor(user, request.card_id)
|
||||||
card_product: CardProduct = await self.session.scalar(
|
card_product: CardProduct = await self.session.scalar(
|
||||||
select(CardProduct)
|
select(CardProduct)
|
||||||
.where(CardProduct.card_id == request.card_id,
|
.where(CardProduct.card_id == request.card_id,
|
||||||
@@ -1106,11 +1120,12 @@ class CardsService(BaseService):
|
|||||||
|
|
||||||
async def add_kit_to_card_product(
|
async def add_kit_to_card_product(
|
||||||
self,
|
self,
|
||||||
user: Union[User, dict],
|
user: UserUnion,
|
||||||
request: CardProductAddKitRequest
|
request: CardProductAddKitRequest
|
||||||
) -> CardProductAddKitResponse:
|
) -> CardProductAddKitResponse:
|
||||||
try:
|
try:
|
||||||
self.grant_access(user, request.card_id)
|
verify_user_deal_editor(user, request.card_id)
|
||||||
|
|
||||||
service_service = ServiceService(self.session)
|
service_service = ServiceService(self.session)
|
||||||
kit = await service_service.get_kit_by_id(request.kit_id)
|
kit = await service_service.get_kit_by_id(request.kit_id)
|
||||||
if not kit:
|
if not kit:
|
||||||
|
|||||||
@@ -68,19 +68,17 @@ class CardGroupService(BaseService):
|
|||||||
)
|
)
|
||||||
if not group:
|
if not group:
|
||||||
return CardGroupUpdateResponse(ok=False, message="Группа не найдена")
|
return CardGroupUpdateResponse(ok=False, message="Группа не найдена")
|
||||||
# update by dictionary
|
|
||||||
request_dict = request.data.model_dump()
|
|
||||||
request_dict.pop("bill_request", None)
|
|
||||||
|
|
||||||
update_stmt = (
|
update_stmt = (
|
||||||
update(CardGroup)
|
update(CardGroup)
|
||||||
.where(CardGroup.id == request.data.id)
|
.where(CardGroup.id == request.data.id)
|
||||||
.values(**request_dict)
|
.values(name=request.data.name, lexorank=request.data.lexorank)
|
||||||
)
|
)
|
||||||
await self.session.execute(update_stmt)
|
await self.session.execute(update_stmt)
|
||||||
await self.session.commit()
|
await self.session.commit()
|
||||||
return CardGroupUpdateResponse(ok=True, message="Группа успешно обновлена")
|
return CardGroupUpdateResponse(ok=True, message="Группа успешно обновлена")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
await self.session.rollback()
|
await self.session.rollback()
|
||||||
return CardGroupUpdateResponse(ok=False, message=str(e))
|
return CardGroupUpdateResponse(ok=False, message=str(e))
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ from external.chat.chat_client import ChatClient
|
|||||||
from external.chat.schemas import *
|
from external.chat.schemas import *
|
||||||
from external.kafka.services.producer_service import ProducerService
|
from external.kafka.services.producer_service import ProducerService
|
||||||
from models import Message, Chat, MessageStatus, TgGroup, Client, Card, MessageFile, User
|
from models import Message, Chat, MessageStatus, TgGroup, Client, Card, MessageFile, User
|
||||||
|
from schemas.auth import UserUnion
|
||||||
from schemas.chat import *
|
from schemas.chat import *
|
||||||
from services.base import BaseService
|
from services.base import BaseService
|
||||||
|
from utils.auth import verify_user_deal_editor
|
||||||
|
|
||||||
|
|
||||||
class ChatService(BaseService):
|
class ChatService(BaseService):
|
||||||
@@ -163,7 +165,9 @@ class ChatService(BaseService):
|
|||||||
|
|
||||||
return CreateChatResponse(ok=True, message='Чат успешно создан')
|
return CreateChatResponse(ok=True, message='Чат успешно создан')
|
||||||
|
|
||||||
async def create_chat(self, request: CreateChatRequest) -> CreateChatResponse:
|
async def create_chat(self, request: CreateChatRequest, user: UserUnion) -> CreateChatResponse:
|
||||||
|
verify_user_deal_editor(user, request.card_id)
|
||||||
|
|
||||||
client: Optional[Client] = await self.session.get(Client, request.client_id)
|
client: Optional[Client] = await self.session.get(Client, request.client_id)
|
||||||
if not client:
|
if not client:
|
||||||
return CreateChatResponse(ok=False, message=f'Клиент с ID {request.client_id} не найден')
|
return CreateChatResponse(ok=False, message=f'Клиент с ID {request.client_id} не найден')
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from sqlalchemy.orm import joinedload, selectinload, noload
|
|||||||
|
|
||||||
from models import Client, ClientDetails, User, ResidualPallet, ResidualBox, ResidualProduct, Product
|
from models import Client, ClientDetails, User, ResidualPallet, ResidualBox, ResidualProduct, Product
|
||||||
from schemas.client import *
|
from schemas.client import *
|
||||||
|
from services.auth import AuthService
|
||||||
from services.base import BaseService
|
from services.base import BaseService
|
||||||
|
|
||||||
|
|
||||||
@@ -165,3 +166,8 @@ class ClientService(BaseService):
|
|||||||
return ClientDeleteResponse(ok=True, message='Клиент удален')
|
return ClientDeleteResponse(ok=True, message='Клиент удален')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return ClientDeleteResponse(ok=False, message=str(e))
|
return ClientDeleteResponse(ok=False, message=str(e))
|
||||||
|
|
||||||
|
async def create_guest_url(self, client_id: int) -> CreateGuestUrlResponse:
|
||||||
|
access_token = AuthService(self.session).create_client_guest_token(client_id)
|
||||||
|
url = f"leads?accessToken={access_token}"
|
||||||
|
return CreateGuestUrlResponse(ok=True, message='Ссылка успешно создана!', url=url)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
import starlette.status
|
||||||
from fastapi import HTTPException, UploadFile
|
from fastapi import HTTPException, UploadFile
|
||||||
from sqlalchemy import select, func, Integer, update, or_
|
from sqlalchemy import select, func, Integer, update, or_
|
||||||
from sqlalchemy.orm import selectinload, Query
|
from sqlalchemy.orm import selectinload, Query
|
||||||
@@ -10,16 +11,20 @@ from backend import config
|
|||||||
from barcodes.images_uploader import BarcodeImagesUploader
|
from barcodes.images_uploader import BarcodeImagesUploader
|
||||||
from barcodes.pdf.pdf_maker import PdfMaker
|
from barcodes.pdf.pdf_maker import PdfMaker
|
||||||
from external.s3_uploader.uploader import S3Uploader
|
from external.s3_uploader.uploader import S3Uploader
|
||||||
|
from models import User
|
||||||
from models.product import Product, ProductImage, ProductBarcodeImage
|
from models.product import Product, ProductImage, ProductBarcodeImage
|
||||||
|
from schemas.auth import UserUnion
|
||||||
from schemas.base import PaginationSchema
|
from schemas.base import PaginationSchema
|
||||||
from schemas.product import *
|
from schemas.product import *
|
||||||
from services.base import BaseService
|
from services.base import BaseService
|
||||||
|
from utils.auth import verify_user_deal_editor
|
||||||
from utils.dependecies import is_valid_pagination
|
from utils.dependecies import is_valid_pagination
|
||||||
|
|
||||||
|
|
||||||
class ProductService(BaseService):
|
class ProductService(BaseService):
|
||||||
|
|
||||||
async def create(self, request: ProductCreateRequest) -> ProductCreateResponse:
|
async def create(self, request: ProductCreateRequest, user: UserUnion) -> ProductCreateResponse:
|
||||||
|
verify_user_deal_editor(user)
|
||||||
|
|
||||||
# Creating product
|
# Creating product
|
||||||
product_dict = request.dict()
|
product_dict = request.dict()
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from sqlalchemy import select, and_, func
|
from sqlalchemy import select, and_, func, exists, or_
|
||||||
|
|
||||||
from models import CardStatus, Card
|
from models import CardStatus, Card, CardStatusHistory
|
||||||
from schemas.status import *
|
from schemas.status import *
|
||||||
from services.base import BaseService
|
from services.base import BaseService
|
||||||
|
|
||||||
@@ -106,6 +106,18 @@ class StatusService(BaseService):
|
|||||||
statuses[-2].is_finishing = True
|
statuses[-2].is_finishing = True
|
||||||
statuses[-1].is_finishing = False
|
statuses[-1].is_finishing = False
|
||||||
|
|
||||||
|
async def _status_has_history(self, status_id: int) -> bool:
|
||||||
|
stmt = (
|
||||||
|
select(exists(CardStatusHistory))
|
||||||
|
.where(
|
||||||
|
or_(
|
||||||
|
CardStatusHistory.to_status_id == status_id,
|
||||||
|
CardStatusHistory.from_status_id == status_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return (await self.session.scalars(stmt)).first()
|
||||||
|
|
||||||
async def delete_status(self, status_id: int) -> DeleteStatusResponse:
|
async def delete_status(self, status_id: int) -> DeleteStatusResponse:
|
||||||
status = await self._get_status_by_id(status_id)
|
status = await self._get_status_by_id(status_id)
|
||||||
if not status:
|
if not status:
|
||||||
@@ -119,7 +131,8 @@ class StatusService(BaseService):
|
|||||||
await self._set_finishing_flag_to_prev_status(status)
|
await self._set_finishing_flag_to_prev_status(status)
|
||||||
|
|
||||||
count_deals = await self._count_deals(status_id)
|
count_deals = await self._count_deals(status_id)
|
||||||
if count_deals == 0:
|
exist_in_history = await self._status_has_history(status_id)
|
||||||
|
if count_deals == 0 and not exist_in_history:
|
||||||
await self.session.delete(status)
|
await self.session.delete(status)
|
||||||
else:
|
else:
|
||||||
status.is_deleted = True
|
status.is_deleted = True
|
||||||
|
|||||||
46
utils/auth.py
Normal file
46
utils/auth.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import starlette.status
|
||||||
|
from fastapi import HTTPException
|
||||||
|
|
||||||
|
from constants import DEALS_VIEWER, DEAL_EDITOR
|
||||||
|
from models import User
|
||||||
|
from schemas.auth import UserUnion
|
||||||
|
|
||||||
|
|
||||||
|
def raise_403():
|
||||||
|
raise HTTPException(status_code=starlette.status.HTTP_403_FORBIDDEN, detail='Forbidden')
|
||||||
|
|
||||||
|
|
||||||
|
def verify_user(user: UserUnion, raising: bool = True) -> bool:
|
||||||
|
if type(user) is User:
|
||||||
|
return True
|
||||||
|
if raising:
|
||||||
|
raise_403()
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def verify_user_viewer(user: UserUnion, client_id: Optional[int] = None, raising: bool = True) -> bool:
|
||||||
|
is_regular_user: bool = type(user) is User
|
||||||
|
is_viewer: bool = isinstance(user, dict) and user['sub'] == DEALS_VIEWER
|
||||||
|
if client_id is not None:
|
||||||
|
is_viewer = is_viewer and int(user['client_id']) == client_id
|
||||||
|
|
||||||
|
if is_regular_user or is_viewer:
|
||||||
|
return True
|
||||||
|
if raising:
|
||||||
|
raise_403()
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def verify_user_deal_editor(user: UserUnion, deal_id: Optional[int] = None, raising: bool = True) -> bool:
|
||||||
|
is_regular_user: bool = type(user) is User
|
||||||
|
is_deal_editor: bool = isinstance(user, dict) and user['sub'] == DEAL_EDITOR
|
||||||
|
if deal_id is not None:
|
||||||
|
is_deal_editor = is_deal_editor and int(user['deal_id']) == deal_id
|
||||||
|
|
||||||
|
if is_regular_user or is_deal_editor:
|
||||||
|
return True
|
||||||
|
if raising:
|
||||||
|
raise_403()
|
||||||
|
return False
|
||||||
Reference in New Issue
Block a user