import uuid from fastapi import ( FastAPI, Depends, HTTPException, status, Query, Body, ) from fastapi.security import OAuth2PasswordRequestForm from sqlmodel import SQLModel, Session, select from typing import Optional, List, Dict from datetime import timedelta, datetime from bff_models import ( Token, RegisterResponse, DashboardCardsResponse, DashboardChartTotalResponse, DashboardChartAgentResponse, StatAgentsResponse, StatReferralsResponse, StatSalesResponse, BillingCardsResponse, BillingChartStatResponse, BillingChartPieResponse, AccountResponse, AccountProfileResponse, AccountProfileUpdateResponse, AccountPasswordChangeResponse, AutoApproveSettingsGetResponse, AutoApproveSettingsUpdateResponse, ApproveTransactionsResult, TgAuthResponse, BillingPayoutsTransactionsResponse, AccountProfileUpdateRequest, AccountPasswordChangeRequest, AutoApproveSettingsRequest, ApproveTransactionsRequest, AgentTransactionResponse, TransactionStatus ) from tg_models import RefAddRequest, RefResponse, RegisterRequest, RefAddResponse, RefStatResponse, StatResponse from sql_models import ( Company, TgAgent, Ref, Sale, AgentTransaction, PartnerTransaction, AgentBalance, Account ) from sqlalchemy import func import hashlib from helpers_bff import ( AUTH_DB_ENGINE, get_db, get_tg_agent_by_tg_id, get_current_account, get_current_tg_agent, create_access_token, verify_password, get_account_by_login, ACCESS_TOKEN_EXPIRE_MINUTES, pwd_context, ) # Создание движка базы данных SQLModel.metadata.create_all(AUTH_DB_ENGINE) # FastAPI app app = FastAPI() @app.post("/register", tags=["partner-tg"], response_model=RegisterResponse) def register(req: RegisterRequest, db: Session = Depends(get_db)): """ Регистрирует нового Telegram-агента в системе. """ tg_id = req.tg_id chat_id = req.chat_id phone = req.phone name = getattr(req, 'name', None) login = getattr(req, 'login', None) company_key = req.company_key print(f'tg_id: {tg_id}, chat_id: {chat_id}, phone: {phone}, name: {name}, login: {login}, company_key: {company_key}') tg_agent = get_tg_agent_by_tg_id(db, tg_id) if tg_agent: raise HTTPException(status_code=400, detail="tg_id already registered") # Поиск компании по ключу company = db.exec(select(Company).where(Company.key == company_key)).first() if not company: raise HTTPException(status_code=400, detail="Компания с таким ключом не найдена") hash_value = hashlib.sha256(f"{tg_id}sold".encode()).hexdigest() new_tg_agent = TgAgent( tg_id=tg_id, chat_id=chat_id, phone=phone, name=name, login=login, hash=hash_value, company_id=company.id ) db.add(new_tg_agent) db.commit() db.refresh(new_tg_agent) return {"msg": "TgAgent registered successfully"} @app.post("/token", response_model=Token, tags=["bff", "token"]) def login_account_for_access_token( form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db) ): """ Авторизует аккаунт и возвращает токен доступа. """ # login: str = Body(...), # password: str = Body(...), account = get_account_by_login(db, form_data.username) if not account or not verify_password(form_data.password, account.password_hash): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect login or password", headers={"WWW-Authenticate": "Bearer"}, ) access_token = create_access_token( data={"sub": account.login}, expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) ) return Token(access_token=access_token, token_type="bearer") @app.get("/ref", response_model=List[RefResponse], tags=["partner-tg"]) def get_refs(current_tg_agent: TgAgent = Depends(get_current_tg_agent), db: Session = Depends(get_db)): """ Возвращает список реферальных ссылок текущего Telegram-агента. """ refs = db.exec(select(Ref).where(Ref.tg_agent_id == current_tg_agent.id)).all() return [RefResponse(ref=r.ref, description=r.description or "") for r in refs] @app.post("/ref/add", tags=["partner-tg"], response_model=RefAddResponse) def add_ref(req: RefAddRequest, current_tg_agent: TgAgent = Depends(get_current_tg_agent), db: Session = Depends(get_db)): """ Добавляет новую реферальную ссылку для текущего Telegram-агента. """ new_ref = Ref( tg_agent_id=current_tg_agent.id, ref=str(uuid.uuid4()), description=req.description ) db.add(new_ref) db.commit() db.refresh(new_ref) return {"ref": new_ref.ref} @app.get("/ref/stat", tags=["partner-tg"], response_model=RefStatResponse) def get_ref_stat(current_tg_agent: TgAgent = Depends(get_current_tg_agent), db: Session = Depends(get_db)): """ Возвращает статистику по реферальным ссылкам текущего Telegram-агента. """ # 1. Получаем все реферальные ссылки пользователя refs = db.exec(select(Ref).where(Ref.tg_agent_id == current_tg_agent.id)).all() result = [] for ref in refs: # 2. Для каждой ссылки считаем продажи и сумму sales = db.exec(select(Sale).where(Sale.ref == ref.id)).all() sales_count = len(sales) income = sum(sale.crediting for sale in sales) result.append({ "description": ref.description or "", "sales": sales_count, "income": income }) return {"refData": result} @app.get("/stat", tags=["partner-tg"], response_model=StatResponse) def get_stat(current_tg_agent: TgAgent = Depends(get_current_tg_agent), db: Session = Depends(get_db)): """ Возвращает общую статистику для текущего Telegram-агента. """ # 1. Получаем все реферальные ссылки пользователя refs = db.exec(select(Ref).where(Ref.tg_agent_id == current_tg_agent.id)).all() ref_ids = [r.id for r in refs] # 2. Считаем totalSales (продажи по всем рефам пользователя) total_sales = db.exec(select(Sale).where(Sale.ref.in_(ref_ids))).all() totalSales = len(total_sales) totalIncome = sum(sale.crediting for sale in total_sales) # Заменено получение доступного остатка из AgentBalance agent_balance = db.exec(select(AgentBalance).where(AgentBalance.tg_agent_id == current_tg_agent.id)).first() availableWithdrawal = agent_balance.available_balance if agent_balance else 0.0 return { "totalSales": totalSales, "totalIncome": totalIncome, "availableWithdrawal": availableWithdrawal } @app.get("/dashboard/cards", tags=["bff", "dashboard"], response_model=DashboardCardsResponse) def get_dashboard_cards(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)): """ Возвращает данные для карточек на главной панели (dashboard) пользователя, включая общий доход, выплаты, активных рефералов, ожидающие выплаты и общее количество продаж. """ # 1. Общий доход - сумма всех Sale.cost total_revenue = db.exec(select(Sale).where(Sale.company_id == current_account.company_id)).all() totalRevenue = sum(sale.cost for sale in total_revenue) # 2. Общие выплаты - сумма всех Sale.crediting totalPayouts = sum(sale.crediting for sale in total_revenue) # 3. Активные рефералы - количество уникальных TgAgent.tg_id unique_agents = db.exec(select(TgAgent.tg_id).where(TgAgent.company_id == current_account.company_id)).all() activeReferrals = len(set(unique_agents)) # 4. Ожидающие выплаты - сумма AgentTransaction со статусом 'waiting' pending_agent_transactions = db.exec(select(AgentTransaction).join(TgAgent).where(TgAgent.company_id == current_account.company_id).where(AgentTransaction.status == 'waiting')).all() pendingPayouts = sum(t.amount for t in pending_agent_transactions) # 5. Количество продаж totalSales = len(total_revenue) return { "totalRevenue": totalRevenue, "totalPayouts": totalPayouts, "activeReferrals": activeReferrals, "pendingPayouts": pendingPayouts, "totalSales": totalSales } @app.get("/dashboard/chart/total", tags=["bff", "dashboard"], response_model=DashboardChartTotalResponse) def get_dashboard_chart_total(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)): """ Возвращает данные для графика "Общий доход и продажи по датам" на главной панели (dashboard). """ # Группируем продажи по дате (день) result = db.exec( select( func.strftime('%Y-%m-%d', Sale.create_dttm).label('date'), func.sum(Sale.cost).label('revenue'), func.count(Sale.id).label('sales') ).where(Sale.company_id == current_account.company_id).group_by(func.strftime('%Y-%m-%d', Sale.create_dttm)) .order_by(func.strftime('%Y-%m-%d', Sale.create_dttm)) ).all() # Преобразуем результат в нужный формат data = [ {"date": row.date, "revenue": row.revenue or 0, "sales": row.sales or 0} for row in result ] return DashboardChartTotalResponse(items=data) @app.get("/dashboard/chart/agent", tags=["bff", "dashboard"], response_model=DashboardChartAgentResponse) def get_dashboard_chart_agent(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)): """ Возвращает данные для графика "Продажи по агентам" на главной панели (dashboard). """ # Получаем всех агентов agents = db.exec(select(TgAgent).where(TgAgent.company_id == current_account.company_id)).all() result = [] for agent in agents: # Получаем все рефы этого агента refs = db.exec(select(Ref).where(Ref.tg_agent_id == agent.id)).all() ref_ids = [r.id for r in refs] if not ref_ids: result.append({ "name": agent.name or f"Агент {agent.id}", "count": 0, "sum": 0.0 }) continue # Получаем все продажи по этим рефам sales = db.exec(select(Sale).where(Sale.ref.in_(ref_ids))).all() sales_count = len(sales) sales_sum = sum(sale.cost for sale in sales) result.append({ "name": agent.name or f"Агент {agent.id}", "count": sales_count, "sum": sales_sum }) return DashboardChartAgentResponse(items=result) @app.get("/stat/agents", tags=["bff", "stat"], response_model=StatAgentsResponse) def get_agents_stat( db: Session = Depends(get_db), date_start: str = Query(None), date_end: str = Query(None), current_account: Account = Depends(get_current_account), ): """ Возвращает статистику по агентам компании, включая количество рефералов, продаж, сумму продаж и начислений. Возможна фильтрация по дате создания агентов. """ agents_query = select(TgAgent).where(TgAgent.company_id == current_account.company_id) if date_start: agents_query = agents_query.where(TgAgent.create_dttm >= date_start) if date_end: agents_query = agents_query.where(TgAgent.create_dttm <= date_end) agents = db.exec(agents_query).all() result = [] for agent in agents: refs = db.exec(select(Ref).where(Ref.tg_agent_id == agent.id)).all() ref_ids = [r.id for r in refs] ref_count = len(ref_ids) if not ref_ids: result.append({ "name": agent.name or f"Агент {agent.id}", "refCount": 0, "salesCount": 0, "salesSum": 0.0, "crediting": 0.0 }) continue sales = db.exec(select(Sale).where(Sale.ref.in_(ref_ids))).all() sales_count = len(sales) sales_sum = sum(sale.cost for sale in sales) crediting_sum = sum(sale.crediting for sale in sales) result.append({ "name": agent.name or f"Агент {agent.id}", "refCount": ref_count, "salesCount": sales_count, "salesSum": sales_sum, "crediting": crediting_sum }) return StatAgentsResponse(items=result) @app.get("/stat/referrals", tags=["bff", "stat"], response_model=StatReferralsResponse) def get_referrals_stat( db: Session = Depends(get_db), date_start: str = Query(None), date_end: str = Query(None), current_account: Account = Depends(get_current_account), ): """ Возвращает статистику по реферальным ссылкам компании, включая имя агента, описание, сумму и количество продаж. Возможна фильтрация по дате создания рефералов. """ refs_query = select(Ref).join(TgAgent).where(TgAgent.company_id == current_account.company_id) if date_start: refs_query = refs_query.where(Ref.create_dttm >= date_start) if date_end: refs_query = refs_query.where(Ref.create_dttm <= date_end) refs = db.exec(refs_query).all() result = [] for ref in refs: agent = db.exec(select(TgAgent).where(TgAgent.id == ref.tg_agent_id)).first() sales = db.exec(select(Sale).where(Sale.ref == ref.id)).all() sales_count = len(sales) sales_sum = sum(sale.cost for sale in sales) result.append({ "ref": ref.ref, "agent": agent.name if agent and agent.name else f"Агент {ref.tg_agent_id}", "description": ref.description or "", "salesSum": sales_sum, "salesCount": sales_count }) return StatReferralsResponse(items=result) @app.get("/stat/sales", tags=["bff", "stat"], response_model=StatSalesResponse) def get_sales_stat( db: Session = Depends(get_db), date_start: str = Query(None), date_end: str = Query(None), current_account: Account = Depends(get_current_account), ): """ Возвращает статистику по продажам компании, включая ID продажи, стоимость, начисления, реферала и имя агента. Возможна фильтрация по дате создания продаж. """ sales_query = select(Sale).where(Sale.company_id == current_account.company_id) if date_start: sales_query = sales_query.where(Sale.create_dttm >= date_start) if date_end: sales_query = sales_query.where(Sale.create_dttm <= date_end) sales = db.exec(sales_query).all() ref_ids = list(set(sale.ref for sale in sales)) refs = db.exec(select(Ref).where(Ref.id.in_(ref_ids))).all() if ref_ids else [] ref_map = {ref.id: ref for ref in refs} agent_ids = list(set(ref.tg_agent_id for ref in refs)) if refs else [] agents = db.exec(select(TgAgent).where(TgAgent.id.in_(agent_ids))).all() if agent_ids else [] agent_map = {agent.id: agent for agent in agents} result = [] for sale in sales: ref_obj = ref_map.get(sale.ref) agent_obj = agent_map.get(ref_obj.tg_agent_id) if ref_obj else None result.append({ "saleId": sale.sale_id, "cost": sale.cost, "crediting": sale.crediting, "ref": ref_obj.ref if ref_obj else None, "name": agent_obj.name if agent_obj else None }) return StatSalesResponse(items=result) @app.get("/billing/cards", tags=["bff", "billing"], response_model=BillingCardsResponse) def get_billing_cards(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)): """ Возвращает ключевые показатели биллинга для компании, включая общий заработок, общие выплаты и доступные к выводу средства. """ # 1. cost - Общий заработок (сумма всех Sale.cost) sales = db.exec(select(Sale).where(Sale.company_id == current_account.company_id)).all() cost = sum(sale.cost for sale in sales) # 2. crediting - Общие выплаты (сумма PartnerTransaction типа 'agent_payout' со статусом 'done') completed_payouts = db.exec(select(PartnerTransaction).where(PartnerTransaction.type == 'agent_payout').where(PartnerTransaction.status == 'done').where(PartnerTransaction.company_id == current_account.company_id)).all() crediting = sum(t.amount for t in completed_payouts) # 3. pendingPayouts - Доступно к выводу всеми партнерами (сумма всех доступных балансов агентов) agent_balances = db.exec(select(AgentBalance).join(TgAgent).where(TgAgent.company_id == current_account.company_id)).all() pendingPayouts = sum(balance.available_balance for balance in agent_balances) return { "cost": cost, "crediting": crediting, "pendingPayouts": pendingPayouts } @app.get("/billing/payouts/transactions", tags=["bff", "billing"], response_model=BillingPayoutsTransactionsResponse) def get_billing_payouts_transactions( db: Session = Depends(get_db), date_start: str = Query(None), date_end: str = Query(None), current_account: Account = Depends(get_current_account), ): """ Возвращает список транзакций выплат для компании текущего пользователя. Возможна фильтрация по дате создания. """ # Используем AgentTransaction вместо Transaction # Явно выбираем обе модели для корректной распаковки query = select(AgentTransaction, TgAgent).join(TgAgent).where(TgAgent.company_id == current_account.company_id) if date_start: query = query.where(AgentTransaction.create_dttm >= date_start) if date_end: query = query.where(AgentTransaction.create_dttm <= date_end) # Заказываем по дате создания query = query.order_by(AgentTransaction.create_dttm.desc()) # Выполняем запрос и формируем результат results = db.exec(query).all() result = [] for agent_trans, agent in results: try: status_enum = TransactionStatus(agent_trans.status) except ValueError: # Если статус из БД не соответствует Enum, используем статус ERROR status_enum = TransactionStatus.ERROR result.append({ "id": agent_trans.transaction_group, # Используем id, как и запрошено "amount": agent_trans.amount, "agent": agent.name if agent else None, "status": status_enum, "create_dttm": agent_trans.create_dttm, "update_dttm": agent_trans.update_dttm, }) return BillingPayoutsTransactionsResponse(items=result) @app.get("/billing/chart/stat", tags=["bff", "billing"], response_model=BillingChartStatResponse) def get_billing_chart_stat(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)): """ Возвращает статистику выплат по датам и статусам для компании текущего пользователя. """ # Группируем агентские транзакции по дате (день) и статусу result = db.exec( select( func.strftime('%Y-%m-%d', AgentTransaction.create_dttm).label('date'), AgentTransaction.status.label('status'), func.count(AgentTransaction.id).label('count') ).join(TgAgent).where(TgAgent.company_id == current_account.company_id).group_by( func.strftime('%Y-%m-%d', AgentTransaction.create_dttm), AgentTransaction.status ).order_by( func.strftime('%Y-%m-%d', AgentTransaction.create_dttm), AgentTransaction.status ) ).all() data = [ {"date": row.date, "status": row.status, "count": row.count} for row in result ] return BillingChartStatResponse(items=data) @app.get("/billing/chart/pie", tags=["bff", "billing"], response_model=BillingChartPieResponse) def get_billing_chart_pie(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)): """ Возвращает круговую диаграмму статистики выплат по статусам для компании текущего пользователя. """ # Группируем агентские транзакции по статусу result = db.exec( select( AgentTransaction.status.label('status'), func.count(AgentTransaction.id).label('count') ).join(TgAgent).where(TgAgent.company_id == current_account.company_id).group_by(AgentTransaction.status) ).all() data = [ {"status": row.status, "count": row.count} for row in result ] return BillingChartPieResponse(items=data) @app.post("/tg_auth", tags=["partner-tg"], response_model=TgAuthResponse) def tg_auth(hash: str = Body(..., embed=True), db: Session = Depends(get_db)): """ Авторизует Telegram-агента по хешу. """ tg_agent = db.exec(select(TgAgent).where(TgAgent.hash == hash)).first() if not tg_agent: raise HTTPException(status_code=401, detail="Hash not found") return {"msg": "Auth success", "tg_id": tg_agent.tg_id} # --- Новый функционал для Account --- @app.get("/account", tags=["bff", "account"], response_model=AccountResponse) def get_account(current_account: Account = Depends(get_current_account)): """ Возвращает базовую информацию об аккаунте текущего пользователя (имя и фамилию). """ return { "firstName": current_account.firstName, "surname": current_account.surname } @app.get("/account/profile", tags=["bff", "account"], response_model=AccountProfileResponse) def get_account_profile(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)): """ Возвращает полную информацию о профиле аккаунта текущего пользователя, включая данные компании. """ company = db.exec(select(Company).where(Company.id == current_account.company_id)).first() if not company: raise HTTPException(status_code=404, detail="Компания не найдена") return { "firstName": current_account.firstName, "surname": current_account.surname, "phone": current_account.phone, "email": current_account.email, "create_dttm": current_account.create_dttm, "company": { "name": company.name, "key": company.key, "commission": company.commission } } @app.post("/account/profile", tags=["bff", "account"], response_model=AccountProfileUpdateResponse) def update_account_profile( req: AccountProfileUpdateRequest, current_account: Account = Depends(get_current_account), db: Session = Depends(get_db) ): """ Обновляет информацию профиля текущего пользователя. """ # Проверка, что все поля заполнены (Pydantic уже валидирует email и обязательность) if not req.firstName.strip() or not req.surname.strip() or not req.email or not req.phone.strip(): raise HTTPException(status_code=400, detail="Все поля должны быть заполнены") # Обновляем поля current_account.firstName = req.firstName.strip() current_account.surname = req.surname.strip() current_account.email = req.email current_account.phone = req.phone.strip() db.add(current_account) db.commit() db.refresh(current_account) return {"msg": "Профиль обновлён успешно"} @app.post("/account/password", tags=["bff", "account"], response_model=AccountPasswordChangeResponse) def change_account_password( req: AccountPasswordChangeRequest, current_account: Account = Depends(get_current_account), db: Session = Depends(get_db) ): """ Изменяет пароль текущего пользователя. """ # Проверяем текущий пароль if not verify_password(req.currentPassword, current_account.password_hash): raise HTTPException(status_code=400, detail="Текущий пароль неверный") # Проверяем, что новый пароль не пустой и отличается от текущего if not req.newPassword.strip(): raise HTTPException(status_code=400, detail="Новый пароль не может быть пустым") if verify_password(req.newPassword, current_account.password_hash): raise HTTPException(status_code=400, detail="Новый пароль не должен совпадать с текущим") # Хешируем и сохраняем новый пароль current_account.password_hash = pwd_context.hash(req.newPassword) db.add(current_account) db.commit() db.refresh(current_account) return {"msg": "Пароль успешно изменён"} # --- Новый функционал для агентских транзакций партнера --- @app.get("/account/agent-transaction", response_model=List[AgentTransactionResponse], tags=["bff", "account"]) def get_account_agent_transactions( statuses: Optional[List[TransactionStatus]] = Query(None), date_start: str = Query(None), date_end: str = Query(None), current_account: Account = Depends(get_current_account), db: Session = Depends(get_db) ): """ Возвращает список агентских транзакций для компании текущего пользователя, с возможностью фильтрации по статусу и дате создания. """ # Получаем ID компании текущего аккаунта company_id = current_account.company_id # Строим базовый запрос: выбрать AgentTransaction и TgAgent, связанные с агентами этой компании query = select(AgentTransaction, TgAgent).join(TgAgent).where(TgAgent.company_id == company_id) # Если переданы статусы, добавляем фильтрацию по статусам if statuses: query = query.where(AgentTransaction.status.in_(statuses)) # Если передана дата начала, добавляем фильтрацию по дате создания >= date_start if date_start: query = query.where(AgentTransaction.create_dttm >= date_start) # Если передана дата окончания, добавляем фильтрацию по дате создания <= date_end if date_end: query = query.where(AgentTransaction.create_dttm <= date_end) # Выполняем запрос results = db.exec(query).all() # Формируем список ответов в формате AgentTransactionResponse agent_transactions_response = [] for agent_trans, agent in results: try: status_enum = TransactionStatus(agent_trans.status) except ValueError: # Если статус из БД не соответствует Enum, используем статус ERROR status_enum = TransactionStatus.ERROR agent_transactions_response.append( AgentTransactionResponse( amount=agent_trans.amount, status=status_enum, transaction_group=agent_trans.transaction_group, create_dttm=agent_trans.create_dttm, update_dttm=agent_trans.update_dttm, agent_name=agent.name ) ) return agent_transactions_response @app.get("/account/auto-approve", tags=["bff", "account"], response_model=AutoApproveSettingsGetResponse) def get_auto_approve_settings( current_account: Account = Depends(get_current_account), db: Session = Depends(get_db) ): """ Возвращает текущую настройку автоматического подтверждения для компании пользователя. """ company = db.exec(select(Company).where(Company.id == current_account.company_id)).first() if not company: raise HTTPException(status_code=404, detail="Компания не найдена") return {"auto_approve_transactions": company.auto_approve_transactions} @app.post("/account/auto-approve", tags=["bff", "account"], response_model=AutoApproveSettingsUpdateResponse) def update_auto_approve_settings( req: AutoApproveSettingsRequest, current_account: Account = Depends(get_current_account), db: Session = Depends(get_db) ): """ Обновляет настройку автоматического подтверждения транзакций для компании пользователя. При необходимости переводит транзакции из 'waiting' в 'new'. """ company = db.exec(select(Company).where(Company.id == current_account.company_id)).first() if not company: raise HTTPException(status_code=404, detail="Компания не найдена") company.auto_approve_transactions = req.auto_approve company.update_dttm = datetime.utcnow() db.add(company) if req.apply_to_current and req.auto_approve: # Применяем только если авто-аппрув включается и запрошено применение к текущим # Находим все агентские транзакции компании в статусе 'waiting' agent_transactions_to_update = db.exec( select(AgentTransaction) .join(TgAgent) .where(TgAgent.company_id == company.id) .where(AgentTransaction.status == TransactionStatus.WAITING) ).all() for agent_trans in agent_transactions_to_update: agent_trans.status = TransactionStatus.NEW agent_trans.update_dttm = datetime.utcnow() db.add(agent_trans) # Находим соответствующие партнерские транзакции и обновляем их статус partner_transactions_to_update = db.exec( select(PartnerTransaction) .where(PartnerTransaction.agent_transaction_id == agent_trans.id) .where(PartnerTransaction.status == TransactionStatus.PROCESS) ).all() for partner_trans in partner_transactions_to_update: partner_trans.status = TransactionStatus.NEW partner_trans.update_dttm = datetime.utcnow() db.add(partner_trans) db.commit() db.refresh(company) return {"msg": "Настройка автоматического подтверждения обновлена", "auto_approve_transactions": company.auto_approve_transactions} @app.post("/account/approve-transactions", tags=["bff", "account"], response_model=ApproveTransactionsResult) def approve_agent_transactions( req: ApproveTransactionsRequest, current_account: Account = Depends(get_current_account), db: Session = Depends(get_db) ): """ Утверждение выбранных агентских транзакций для компании текущего пользователя. Переводит транзакции из статуса 'waiting' в 'new'. """ company_id = current_account.company_id approved_count = 0 if not req.transaction_ids: return {"msg": "Нет транзакций для утверждения", "approved_count": 0} # Find transactions belonging to the company and with specified IDs and statuses transactions_to_approve = db.exec( select(AgentTransaction) .join(TgAgent) .where(TgAgent.company_id == company_id) .where(AgentTransaction.transaction_group.in_(req.transaction_ids)) .where(AgentTransaction.status == TransactionStatus.WAITING) ).all() for agent_trans in transactions_to_approve: agent_trans.status = TransactionStatus.NEW agent_trans.update_dttm = datetime.utcnow() db.add(agent_trans) approved_count += 1 db.commit() return {"msg": f"Переведено в статус NEW {approved_count} транзакций", "approved_count": approved_count}