feat: Add new command

This commit is contained in:
2026-06-10 17:01:51 +03:00
parent f9dc59729c
commit dc05528405
5 changed files with 74 additions and 0 deletions

View File

@@ -23,6 +23,7 @@ from src.application.commands.organization_wallet_commands import (
from src.application.commands.purchase_request_commands import ( from src.application.commands.purchase_request_commands import (
ListPurchaseRequestsCommand, ListPurchaseRequestsCommand,
GetPurchaseRequestCommand, GetPurchaseRequestCommand,
UpdatePurchaseRequestCommand,
UpdatePurchaseRequestStatusCommand, UpdatePurchaseRequestStatusCommand,
SetPurchaseRequestQuoteCommand, SetPurchaseRequestQuoteCommand,
) )
@@ -41,6 +42,7 @@ __all__ = [
'UpdateOrganizationCommand', 'UpdateOrganizationCommand',
'ListPurchaseRequestsCommand', 'ListPurchaseRequestsCommand',
'GetPurchaseRequestCommand', 'GetPurchaseRequestCommand',
'UpdatePurchaseRequestCommand',
'UpdatePurchaseRequestStatusCommand', 'UpdatePurchaseRequestStatusCommand',
'SetPurchaseRequestQuoteCommand', 'SetPurchaseRequestQuoteCommand',
'ListOrganizationDocumentsCommand', 'ListOrganizationDocumentsCommand',

View File

@@ -81,10 +81,46 @@ class UpdatePurchaseRequestStatusCommand:
values['tx_hash'] = tx_hash values['tx_hash'] = tx_hash
if status == 'completed': if status == 'completed':
values['completed_at'] = datetime.now(timezone.utc) values['completed_at'] = datetime.now(timezone.utc)
else:
values['completed_at'] = None
return await self._unit_of_work.purchase_request_repository.update(request_id, values=values) return await self._unit_of_work.purchase_request_repository.update(request_id, values=values)
class UpdatePurchaseRequestCommand:
ALLOWED_FIELDS = frozenset({
'status',
'usdt_amount',
'rub_amount',
'exchange_rate',
'service_fee_percent',
'comment',
'admin_comment',
'target_wallet_chain',
'target_wallet_address',
'tx_hash',
'assigned_to',
})
def __init__(self, unit_of_work: IUnitOfWork, logger: ILogger):
self._unit_of_work = unit_of_work
self._logger = logger
@transactional
async def __call__(self, request_id: str, *, values: dict[str, Any]) -> PurchaseRequestEntity:
filtered = {k: v for k, v in values.items() if k in self.ALLOWED_FIELDS}
status = filtered.get('status')
if status is not None:
if status not in VALID_STATUSES:
raise ApplicationException(status_code=400, message='Invalid status')
if status == 'completed':
filtered['completed_at'] = datetime.now(timezone.utc)
else:
filtered['completed_at'] = None
return await self._unit_of_work.purchase_request_repository.update(request_id, values=filtered)
class SetPurchaseRequestQuoteCommand: class SetPurchaseRequestQuoteCommand:
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

View File

@@ -22,6 +22,7 @@ from src.application.commands import (
SearchPartiesCommand, SearchPartiesCommand,
SetPurchaseRequestQuoteCommand, SetPurchaseRequestQuoteCommand,
UpdateOrganizationCommand, UpdateOrganizationCommand,
UpdatePurchaseRequestCommand,
UpdatePurchaseRequestStatusCommand, UpdatePurchaseRequestStatusCommand,
PutOrganizationDocumentCommand, PutOrganizationDocumentCommand,
) )
@@ -186,6 +187,13 @@ def get_update_purchase_request_status_command(
return UpdatePurchaseRequestStatusCommand(uow, logger) return UpdatePurchaseRequestStatusCommand(uow, logger)
def get_update_purchase_request_command(
uow: IUnitOfWork = Depends(get_unit_of_work),
logger: ILogger = Depends(get_logger),
) -> UpdatePurchaseRequestCommand:
return UpdatePurchaseRequestCommand(uow, logger)
def get_set_purchase_request_quote_command( def get_set_purchase_request_quote_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),

View File

@@ -4,6 +4,7 @@ from src.application.commands import (
GetPurchaseRequestCommand, GetPurchaseRequestCommand,
ListPurchaseRequestsCommand, ListPurchaseRequestsCommand,
SetPurchaseRequestQuoteCommand, SetPurchaseRequestQuoteCommand,
UpdatePurchaseRequestCommand,
UpdatePurchaseRequestStatusCommand, UpdatePurchaseRequestStatusCommand,
) )
from src.application.domain.dto import AdminAuthContext from src.application.domain.dto import AdminAuthContext
@@ -12,6 +13,7 @@ from src.presentation.dependencies.commands import (
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,
get_update_purchase_request_command,
get_update_purchase_request_status_command, get_update_purchase_request_status_command,
) )
from src.presentation.schemas.mappers import purchase_request_to_response from src.presentation.schemas.mappers import purchase_request_to_response
@@ -19,6 +21,7 @@ from src.presentation.schemas.organization import (
PurchaseRequestListResponse, PurchaseRequestListResponse,
PurchaseRequestResponse, PurchaseRequestResponse,
SetPurchaseRequestQuoteBody, SetPurchaseRequestQuoteBody,
UpdatePurchaseRequestBody,
UpdatePurchaseRequestStatusBody, UpdatePurchaseRequestStatusBody,
) )
@@ -56,6 +59,17 @@ async def get_purchase_request(
return purchase_request_to_response(item) return purchase_request_to_response(item)
@purchase_requests_router.patch('/{request_id}', response_model=PurchaseRequestResponse)
async def update_purchase_request(
request_id: str,
body: UpdatePurchaseRequestBody,
auth: AdminAuthContext = Depends(require_admin_role('compliance', 'superadmin')),
command: UpdatePurchaseRequestCommand = Depends(get_update_purchase_request_command),
):
item = await command(request_id, values=body.model_dump(exclude_unset=True))
return purchase_request_to_response(item)
@purchase_requests_router.patch('/{request_id}/status', response_model=PurchaseRequestResponse) @purchase_requests_router.patch('/{request_id}/status', response_model=PurchaseRequestResponse)
async def update_purchase_request_status( async def update_purchase_request_status(
request_id: str, request_id: str,

View File

@@ -121,6 +121,20 @@ class UpdatePurchaseRequestStatusBody(BaseModel):
tx_hash: str | None = None tx_hash: str | None = None
class UpdatePurchaseRequestBody(BaseModel):
status: str | None = None
usdt_amount: Decimal | None = Field(default=None, gt=0)
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=None, 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 SetPurchaseRequestQuoteBody(BaseModel): class SetPurchaseRequestQuoteBody(BaseModel):
rub_amount: Decimal = Field(gt=0) rub_amount: Decimal = Field(gt=0)
exchange_rate: Decimal = Field(gt=0) exchange_rate: Decimal = Field(gt=0)