197 lines
6.7 KiB
Python
197 lines
6.7 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import datetime, timezone
|
|
from decimal import Decimal
|
|
from typing import Any
|
|
|
|
from src.application.abstractions import IUnitOfWork
|
|
from src.application.contracts import ILogger
|
|
from src.application.domain.entities.organization import PurchaseRequestEntity
|
|
from src.application.domain.exceptions import ApplicationException
|
|
from src.infrastructure.database.decorators import transactional
|
|
|
|
|
|
VALID_STATUSES = frozenset({
|
|
'submitted', 'in_review', 'quote_sent', 'payment_pending',
|
|
'payment_received', 'usdt_sent', 'completed', 'rejected', 'cancelled',
|
|
})
|
|
|
|
|
|
class ListPurchaseRequestsCommand:
|
|
def __init__(self, unit_of_work: IUnitOfWork, logger: ILogger):
|
|
self._unit_of_work = unit_of_work
|
|
self._logger = logger
|
|
|
|
@transactional
|
|
async def __call__(
|
|
self,
|
|
*,
|
|
status: str | None = None,
|
|
organization_id: str | None = None,
|
|
limit: int = 50,
|
|
offset: int = 0,
|
|
) -> tuple[list[PurchaseRequestEntity], int]:
|
|
items = await self._unit_of_work.purchase_request_repository.list_all(
|
|
status=status,
|
|
organization_id=organization_id,
|
|
limit=limit,
|
|
offset=offset,
|
|
)
|
|
total = await self._unit_of_work.purchase_request_repository.count_all(
|
|
status=status,
|
|
organization_id=organization_id,
|
|
)
|
|
return items, total
|
|
|
|
|
|
class GetPurchaseRequestCommand:
|
|
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) -> PurchaseRequestEntity:
|
|
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:
|
|
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,
|
|
*,
|
|
status: str,
|
|
admin_comment: str | None = None,
|
|
assigned_to: str | None = None,
|
|
tx_hash: str | None = None,
|
|
) -> PurchaseRequestEntity:
|
|
if status not in VALID_STATUSES:
|
|
raise ApplicationException(status_code=400, message='Invalid status')
|
|
|
|
values: dict[str, Any] = {'status': status}
|
|
if admin_comment is not None:
|
|
values['admin_comment'] = admin_comment
|
|
if assigned_to is not None:
|
|
values['assigned_to'] = assigned_to
|
|
if tx_hash is not None:
|
|
values['tx_hash'] = tx_hash
|
|
if status == 'completed':
|
|
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)
|
|
|
|
|
|
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:
|
|
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,
|
|
*,
|
|
rub_amount: Decimal,
|
|
exchange_rate: Decimal,
|
|
service_fee_percent: Decimal | None = None,
|
|
admin_comment: str | None = None,
|
|
) -> PurchaseRequestEntity:
|
|
values: dict[str, Any] = {
|
|
'rub_amount': rub_amount,
|
|
'exchange_rate': exchange_rate,
|
|
'status': 'quote_sent',
|
|
}
|
|
if service_fee_percent is not None:
|
|
values['service_fee_percent'] = service_fee_percent
|
|
if admin_comment is not None:
|
|
values['admin_comment'] = admin_comment
|
|
return await self._unit_of_work.purchase_request_repository.update(request_id, values=values)
|