feat: add create request
This commit is contained in:
@@ -5,6 +5,10 @@ from src.application.domain.entities.organization import PurchaseRequestEntity
|
|||||||
|
|
||||||
|
|
||||||
class IPurchaseRequestRepository(ABC):
|
class IPurchaseRequestRepository(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
async def create(self, values: dict[str, Any]) -> PurchaseRequestEntity:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def get_by_id(self, request_id: str) -> PurchaseRequestEntity:
|
async def get_by_id(self, request_id: str) -> PurchaseRequestEntity:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ from src.application.commands.organization_wallet_commands import (
|
|||||||
ListOrganizationWalletsCommand,
|
ListOrganizationWalletsCommand,
|
||||||
)
|
)
|
||||||
from src.application.commands.purchase_request_commands import (
|
from src.application.commands.purchase_request_commands import (
|
||||||
|
CreatePurchaseRequestCommand,
|
||||||
ListPurchaseRequestsCommand,
|
ListPurchaseRequestsCommand,
|
||||||
GetPurchaseRequestCommand,
|
GetPurchaseRequestCommand,
|
||||||
UpdatePurchaseRequestCommand,
|
UpdatePurchaseRequestCommand,
|
||||||
@@ -40,6 +41,7 @@ __all__ = [
|
|||||||
'GetOrganizationCommand',
|
'GetOrganizationCommand',
|
||||||
'SearchPartiesCommand',
|
'SearchPartiesCommand',
|
||||||
'UpdateOrganizationCommand',
|
'UpdateOrganizationCommand',
|
||||||
|
'CreatePurchaseRequestCommand',
|
||||||
'ListPurchaseRequestsCommand',
|
'ListPurchaseRequestsCommand',
|
||||||
'GetPurchaseRequestCommand',
|
'GetPurchaseRequestCommand',
|
||||||
'UpdatePurchaseRequestCommand',
|
'UpdatePurchaseRequestCommand',
|
||||||
|
|||||||
@@ -54,6 +54,54 @@ class GetPurchaseRequestCommand:
|
|||||||
return await self._unit_of_work.purchase_request_repository.get_by_id(request_id)
|
return await self._unit_of_work.purchase_request_repository.get_by_id(request_id)
|
||||||
|
|
||||||
|
|
||||||
|
class CreatePurchaseRequestCommand:
|
||||||
|
def __init__(self, unit_of_work: IUnitOfWork, logger: ILogger):
|
||||||
|
self._unit_of_work = unit_of_work
|
||||||
|
self._logger = logger
|
||||||
|
|
||||||
|
@transactional
|
||||||
|
async def __call__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
organization_id: str,
|
||||||
|
usdt_amount: Decimal,
|
||||||
|
status: str = 'submitted',
|
||||||
|
rub_amount: Decimal | None = None,
|
||||||
|
exchange_rate: Decimal | None = None,
|
||||||
|
service_fee_percent: Decimal | None = None,
|
||||||
|
comment: str | None = None,
|
||||||
|
admin_comment: str | None = None,
|
||||||
|
target_wallet_chain: str | None = 'ETH',
|
||||||
|
target_wallet_address: str | None = None,
|
||||||
|
tx_hash: str | None = None,
|
||||||
|
assigned_to: str | None = None,
|
||||||
|
) -> PurchaseRequestEntity:
|
||||||
|
if status not in VALID_STATUSES:
|
||||||
|
raise ApplicationException(status_code=400, message='Invalid status')
|
||||||
|
|
||||||
|
await self._unit_of_work.legal_entity_repository.get_by_id(organization_id)
|
||||||
|
values: dict[str, Any] = {
|
||||||
|
'organization_id': organization_id,
|
||||||
|
'status': status,
|
||||||
|
'usdt_amount': usdt_amount,
|
||||||
|
'rub_amount': rub_amount,
|
||||||
|
'exchange_rate': exchange_rate,
|
||||||
|
'service_fee_percent': service_fee_percent,
|
||||||
|
'comment': comment,
|
||||||
|
'admin_comment': admin_comment,
|
||||||
|
'target_wallet_chain': target_wallet_chain,
|
||||||
|
'target_wallet_address': target_wallet_address,
|
||||||
|
'tx_hash': tx_hash,
|
||||||
|
'assigned_to': assigned_to,
|
||||||
|
}
|
||||||
|
if status == 'completed':
|
||||||
|
values['completed_at'] = datetime.now(timezone.utc)
|
||||||
|
|
||||||
|
created = await self._unit_of_work.purchase_request_repository.create(values)
|
||||||
|
self._logger.info(f'Purchase request created id={created.id} org={organization_id}')
|
||||||
|
return created
|
||||||
|
|
||||||
|
|
||||||
class UpdatePurchaseRequestStatusCommand:
|
class UpdatePurchaseRequestStatusCommand:
|
||||||
def __init__(self, unit_of_work: IUnitOfWork, logger: ILogger):
|
def __init__(self, unit_of_work: IUnitOfWork, logger: ILogger):
|
||||||
self._unit_of_work = unit_of_work
|
self._unit_of_work = unit_of_work
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from ulid import ULID
|
||||||
from sqlalchemy import func, select, update
|
from sqlalchemy import func, select, update
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
@@ -45,6 +46,16 @@ class PurchaseRequestRepository(IPurchaseRequestRepository):
|
|||||||
stmt = stmt.where(PurchaseRequestModel.organization_id == organization_id)
|
stmt = stmt.where(PurchaseRequestModel.organization_id == organization_id)
|
||||||
return stmt
|
return stmt
|
||||||
|
|
||||||
|
async def create(self, values: dict[str, Any]) -> PurchaseRequestEntity:
|
||||||
|
model = PurchaseRequestModel(id=str(ULID()), **values)
|
||||||
|
self._session.add(model)
|
||||||
|
try:
|
||||||
|
await self._session.flush()
|
||||||
|
return self._to_entity(model)
|
||||||
|
except SQLAlchemyError as exc:
|
||||||
|
self._logger.exception(str(exc))
|
||||||
|
raise ApplicationException(status_code=500, message='Database error')
|
||||||
|
|
||||||
async def get_by_id(self, request_id: str) -> PurchaseRequestEntity:
|
async def get_by_id(self, request_id: str) -> PurchaseRequestEntity:
|
||||||
res = await self._session.execute(select(PurchaseRequestModel).where(PurchaseRequestModel.id == request_id))
|
res = await self._session.execute(select(PurchaseRequestModel).where(PurchaseRequestModel.id == request_id))
|
||||||
m = res.scalar_one_or_none()
|
m = res.scalar_one_or_none()
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from src.application.commands import (
|
|||||||
AdminLoginCommand,
|
AdminLoginCommand,
|
||||||
AdminLogoutCommand,
|
AdminLogoutCommand,
|
||||||
AdminJwtRefreshCommand,
|
AdminJwtRefreshCommand,
|
||||||
|
CreatePurchaseRequestCommand,
|
||||||
GetAdminMeCommand,
|
GetAdminMeCommand,
|
||||||
CreateOrganizationCommand,
|
CreateOrganizationCommand,
|
||||||
CreateOrganizationWalletsCommand,
|
CreateOrganizationWalletsCommand,
|
||||||
@@ -173,6 +174,13 @@ def get_list_purchase_requests_command(
|
|||||||
return ListPurchaseRequestsCommand(uow, logger)
|
return ListPurchaseRequestsCommand(uow, logger)
|
||||||
|
|
||||||
|
|
||||||
|
def get_create_purchase_request_command(
|
||||||
|
uow: IUnitOfWork = Depends(get_unit_of_work),
|
||||||
|
logger: ILogger = Depends(get_logger),
|
||||||
|
) -> CreatePurchaseRequestCommand:
|
||||||
|
return CreatePurchaseRequestCommand(uow, logger)
|
||||||
|
|
||||||
|
|
||||||
def get_get_purchase_request_command(
|
def get_get_purchase_request_command(
|
||||||
uow: IUnitOfWork = Depends(get_unit_of_work),
|
uow: IUnitOfWork = Depends(get_unit_of_work),
|
||||||
logger: ILogger = Depends(get_logger),
|
logger: ILogger = Depends(get_logger),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from fastapi import APIRouter, Depends, Query, status
|
from fastapi import APIRouter, Depends, Query, status
|
||||||
|
|
||||||
from src.application.commands import (
|
from src.application.commands import (
|
||||||
|
CreatePurchaseRequestCommand,
|
||||||
GetPurchaseRequestCommand,
|
GetPurchaseRequestCommand,
|
||||||
ListPurchaseRequestsCommand,
|
ListPurchaseRequestsCommand,
|
||||||
SetPurchaseRequestQuoteCommand,
|
SetPurchaseRequestQuoteCommand,
|
||||||
@@ -10,6 +11,7 @@ from src.application.commands import (
|
|||||||
from src.application.domain.dto import AdminAuthContext
|
from src.application.domain.dto import AdminAuthContext
|
||||||
from src.presentation.decorators.admin_auth import require_admin_access, require_admin_role
|
from src.presentation.decorators.admin_auth import require_admin_access, require_admin_role
|
||||||
from src.presentation.dependencies.commands import (
|
from src.presentation.dependencies.commands import (
|
||||||
|
get_create_purchase_request_command,
|
||||||
get_get_purchase_request_command,
|
get_get_purchase_request_command,
|
||||||
get_list_purchase_requests_command,
|
get_list_purchase_requests_command,
|
||||||
get_set_purchase_request_quote_command,
|
get_set_purchase_request_quote_command,
|
||||||
@@ -18,6 +20,7 @@ from src.presentation.dependencies.commands import (
|
|||||||
)
|
)
|
||||||
from src.presentation.schemas.mappers import purchase_request_to_response
|
from src.presentation.schemas.mappers import purchase_request_to_response
|
||||||
from src.presentation.schemas.organization import (
|
from src.presentation.schemas.organization import (
|
||||||
|
CreatePurchaseRequestBody,
|
||||||
PurchaseRequestListResponse,
|
PurchaseRequestListResponse,
|
||||||
PurchaseRequestResponse,
|
PurchaseRequestResponse,
|
||||||
SetPurchaseRequestQuoteBody,
|
SetPurchaseRequestQuoteBody,
|
||||||
@@ -49,6 +52,29 @@ async def list_purchase_requests(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@purchase_requests_router.post('', response_model=PurchaseRequestResponse, status_code=status.HTTP_201_CREATED)
|
||||||
|
async def create_purchase_request(
|
||||||
|
body: CreatePurchaseRequestBody,
|
||||||
|
auth: AdminAuthContext = Depends(require_admin_role('compliance', 'superadmin')),
|
||||||
|
command: CreatePurchaseRequestCommand = Depends(get_create_purchase_request_command),
|
||||||
|
):
|
||||||
|
item = await command(
|
||||||
|
organization_id=body.organization_id,
|
||||||
|
usdt_amount=body.usdt_amount,
|
||||||
|
status=body.status,
|
||||||
|
rub_amount=body.rub_amount,
|
||||||
|
exchange_rate=body.exchange_rate,
|
||||||
|
service_fee_percent=body.service_fee_percent,
|
||||||
|
comment=body.comment,
|
||||||
|
admin_comment=body.admin_comment,
|
||||||
|
target_wallet_chain=body.target_wallet_chain,
|
||||||
|
target_wallet_address=body.target_wallet_address,
|
||||||
|
tx_hash=body.tx_hash,
|
||||||
|
assigned_to=body.assigned_to,
|
||||||
|
)
|
||||||
|
return purchase_request_to_response(item)
|
||||||
|
|
||||||
|
|
||||||
@purchase_requests_router.get('/{request_id}', response_model=PurchaseRequestResponse)
|
@purchase_requests_router.get('/{request_id}', response_model=PurchaseRequestResponse)
|
||||||
async def get_purchase_request(
|
async def get_purchase_request(
|
||||||
request_id: str,
|
request_id: str,
|
||||||
|
|||||||
@@ -114,6 +114,21 @@ class DocumentResponse(BaseModel):
|
|||||||
download_url: str | None = None
|
download_url: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class CreatePurchaseRequestBody(BaseModel):
|
||||||
|
organization_id: str
|
||||||
|
usdt_amount: Decimal = Field(gt=0)
|
||||||
|
status: str = 'submitted'
|
||||||
|
rub_amount: Decimal | None = Field(default=None, gt=0)
|
||||||
|
exchange_rate: Decimal | None = Field(default=None, gt=0)
|
||||||
|
service_fee_percent: Decimal | None = Field(default=None, ge=0)
|
||||||
|
comment: str | None = None
|
||||||
|
admin_comment: str | None = None
|
||||||
|
target_wallet_chain: str | None = Field(default='ETH', max_length=16)
|
||||||
|
target_wallet_address: str | None = Field(default=None, max_length=128)
|
||||||
|
tx_hash: str | None = Field(default=None, max_length=128)
|
||||||
|
assigned_to: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class UpdatePurchaseRequestStatusBody(BaseModel):
|
class UpdatePurchaseRequestStatusBody(BaseModel):
|
||||||
status: str
|
status: str
|
||||||
admin_comment: str | None = None
|
admin_comment: str | None = None
|
||||||
|
|||||||
Reference in New Issue
Block a user