from __future__ import annotations from dataclasses import replace from datetime import datetime,timezone from sqlalchemy import desc,select from sqlalchemy.ext.asyncio import AsyncSession from src.application.abstractions.repositories import ISbpWithdrawalRepository from src.application.contracts import ILogger from src.application.domain.entities import SbpWithdrawalEntity from src.application.domain.enums import SbpWithdrawalStatus from src.infrastructure.database.models.sbp_withdrawal import SbpWithdrawal class SbpWithdrawalRepository(ISbpWithdrawalRepository): def __init__(self,session: AsyncSession,logger: ILogger): self._session=session self._logger=logger @staticmethod def _to_entity(model: SbpWithdrawal) -> SbpWithdrawalEntity: return SbpWithdrawalEntity( id=model.id, created_at=model.created_at, updated_at=model.updated_at, user_id=model.user_id, bank_id=model.bank_id, bank_name=model.bank_name, phone=model.phone, usdt_amount=model.usdt_amount, rub_amount=model.rub_amount, usdt_exchange_rate=model.usdt_exchange_rate, service_fee_rate=model.service_fee_rate, service_fee_usdt=model.service_fee_usdt, mozen_order_id=model.mozen_order_id, trace_id=model.trace_id, status=model.status, provider_error=model.provider_error, usdt_received_at=model.usdt_received_at, payout_created_at=model.payout_created_at, payout_completed_at=model.payout_completed_at, ) async def create(self,withdrawal: SbpWithdrawalEntity) -> SbpWithdrawalEntity: values={ 'user_id':withdrawal.user_id, 'bank_id':withdrawal.bank_id, 'bank_name':withdrawal.bank_name, 'phone':withdrawal.phone, 'usdt_amount':withdrawal.usdt_amount, 'rub_amount':withdrawal.rub_amount, 'usdt_exchange_rate':withdrawal.usdt_exchange_rate, 'service_fee_rate':withdrawal.service_fee_rate, 'service_fee_usdt':withdrawal.service_fee_usdt, 'mozen_order_id':withdrawal.mozen_order_id, 'trace_id':withdrawal.trace_id, 'status':withdrawal.status or SbpWithdrawalStatus.CREATED, 'provider_error':withdrawal.provider_error, 'usdt_received_at':withdrawal.usdt_received_at, 'payout_created_at':withdrawal.payout_created_at, 'payout_completed_at':withdrawal.payout_completed_at, } if withdrawal.id is not None: values['id']=withdrawal.id model=SbpWithdrawal(**values) self._session.add(model) await self._session.flush() return replace(withdrawal,id=model.id,created_at=model.created_at,updated_at=model.updated_at) async def get_by_id(self,withdraw_id: str) -> SbpWithdrawalEntity | None: stmt=select(SbpWithdrawal).where(SbpWithdrawal.id==withdraw_id) model=await self._session.scalar(stmt) if model is None: return None return self._to_entity(model) async def get_by_id_for_user(self,*,withdraw_id: str,user_id: str) -> SbpWithdrawalEntity | None: stmt=select(SbpWithdrawal).where(SbpWithdrawal.id==withdraw_id,SbpWithdrawal.user_id==user_id) model=await self._session.scalar(stmt) if model is None: return None return self._to_entity(model) async def list_by_user_id(self,*,user_id: str,limit: int,offset: int) -> list[SbpWithdrawalEntity]: stmt=( select(SbpWithdrawal) .where(SbpWithdrawal.user_id==user_id) .order_by(desc(SbpWithdrawal.created_at)) .limit(limit) .offset(offset) ) result=await self._session.scalars(stmt) return [self._to_entity(model) for model in result.all()] async def update_status( self, *, withdraw_id: str, status: SbpWithdrawalStatus, provider_error: str | None = None, ) -> None: model=await self._session.get(SbpWithdrawal,withdraw_id) if model is None: return model.status=status model.provider_error=provider_error if status==SbpWithdrawalStatus.USDT_RECEIVED: model.usdt_received_at=datetime.now(timezone.utc) if status==SbpWithdrawalStatus.PAYOUT_PROCESSING: model.payout_created_at=datetime.now(timezone.utc) if status==SbpWithdrawalStatus.PAYOUT_COMPLETED: model.payout_completed_at=datetime.now(timezone.utc) await self._session.flush() async def update_payout_created( self, *, withdraw_id: str, mozen_order_id: str | None, status: SbpWithdrawalStatus, provider_error: str | None, ) -> None: model=await self._session.get(SbpWithdrawal,withdraw_id) if model is None: return model.mozen_order_id=mozen_order_id model.status=status model.provider_error=provider_error model.payout_created_at=datetime.now(timezone.utc) if status==SbpWithdrawalStatus.PAYOUT_COMPLETED: model.payout_completed_at=datetime.now(timezone.utc) await self._session.flush()