from datetime import datetime from sqlalchemy import select, and_, func from app.core.logger import make_log from app.core.models import Memory, User, UserBalance, Asset, InternalTransaction from app.core.storage import db_session async def get_user_balance(session, user: User, asset: Asset) -> UserBalance: assert user, "No user" assert asset, "No asset" result = await session.execute(select(UserBalance).where( and_(UserBalance.user_id == user.id, UserBalance.asset_id == asset.id) )) row = result.scalars().first() if not row: user_balance = UserBalance( user_id=user.id, asset_id=asset.id, balance=0, created=datetime.now(), ) session.add(user_balance) await session.commit() return await get_user_balance(session, user, asset) return row async def make_internal_transaction( memory: Memory, user_id: int, asset_id: int, amount: float, is_spent: bool, type: str = "NOT_SPECIFIED", spent_transaction_id: int = None ) -> InternalTransaction: amount = float(amount) is_spent = bool(is_spent) type = str(type)[:256] if amount < 0: if not is_spent: raise Exception(f"Invalid is_spent: {is_spent}, amount: {amount}") elif amount > 0: if is_spent: raise Exception(f"Invalid is_spent: {is_spent}, amount: {amount}") else: raise Exception(f"Invalid amount: {amount}") abs_amount = abs(amount) async with db_session(auto_commit=False) as session: async with memory.transaction(): user = (await session.execute(select(User).where(User.id == user_id))).scalars().first() assert user, "No user" asset = (await session.execute(select(Asset).where(Asset.id == asset_id))).scalars().first() assert asset, "No asset" user_balance = await get_user_balance(session, user, asset) assert user_balance, "No user balance" if is_spent is True: if abs_amount > user_balance.balance: raise Exception(f"Insufficient balance: {user_balance.balance} < {abs_amount}") user_balance.balance = float(user_balance.balance) + amount user_balance.updated = datetime.now() internal_transaction = InternalTransaction( user_id=user.id, asset_id=asset.id, amount=abs_amount, is_spent=is_spent, type=type, spent_transaction_id=spent_transaction_id, created=datetime.now(), ) session.add(internal_transaction) await session.commit() make_log(user, f"Made internal transaction: {'-' if is_spent else ''}{abs_amount} {asset.symbol}, type: {type}")