This commit is contained in:
2025-09-28 19:13:01 +02:00
parent 49edf780b5
commit 541ecb48f2
67 changed files with 5176 additions and 5008 deletions

View File

@@ -0,0 +1,108 @@
from datetime import datetime, timezone
from enum import Enum
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy import select, func
from sqlalchemy.orm import Session
from sqlalchemy.exc import NoResultFound
from app.core.database import get_db
from app.core.auth import get_current_user
from app.models.user import User
from app.models.transaction import Transaction # siehe Modell unten falls noch nicht vorhanden
from app.schemas.transaction import TransactionOut, TransactionCreate # siehe Schemas unten
router = APIRouter(prefix="/admin/transactions", tags=["admin-transactions"])
def require_admin(current=Depends(get_current_user)):
if getattr(current, "role", None) != "admin":
raise HTTPException(status_code=403, detail="Admin only")
return current
@router.get("", response_model=List[TransactionOut])
def list_transactions(
db: Session = Depends(get_db),
_admin=Depends(require_admin),
limit: int = Query(200, ge=1, le=500),
offset: int = Query(0, ge=0),
order: str = Query("desc", pattern="^(asc|desc)$"),
status: Optional[str] = Query(None, pattern="^(waiting|approved|rejected)$"),
):
q = select(Transaction).offset(offset).limit(limit)
if status:
q = q.where(Transaction.status == status)
if order == "desc":
q = q.order_by(Transaction.created_at.desc())
else:
q = q.order_by(Transaction.created_at.asc())
return db.execute(q).scalars().all()
@router.post("", response_model=TransactionOut, status_code=201)
def create_transaction(
body: TransactionCreate,
db: Session = Depends(get_db),
admin=Depends(require_admin),
):
tx = Transaction(
user_id=body.user_id,
amount_cents=body.amount_cents,
note=body.note or "",
status="waiting",
created_at=datetime.now(timezone.utc),
created_by_id=admin.id,
kind="manual",
)
db.add(tx)
db.commit()
db.refresh(tx)
return tx
@router.post("/{tx_id}/approve", response_model=TransactionOut)
def approve_transaction(
tx_id: int,
db: Session = Depends(get_db),
admin=Depends(require_admin),
):
tx = db.get(Transaction, tx_id)
if not tx:
raise HTTPException(404, "Transaktion nicht gefunden")
if tx.status != "waiting":
raise HTTPException(status_code=409, detail=f"Transaktion bereits {tx.status}")
# Nutzer sperren, um Doppelbuchungen zu vermeiden
user = db.execute(
select(User).where(User.id == tx.user_id).with_for_update()
).scalar_one()
new_balance = (user.balance_cents or 0) + tx.amount_cents
if new_balance < 0:
raise HTTPException(400, "Saldo würde negativ Buchung abgelehnt")
user.balance_cents = new_balance
tx.status = "approved"
tx.processed_at = datetime.now(timezone.utc)
tx.processed_by_id = admin.id
db.commit()
db.refresh(tx)
return tx
@router.post("/{tx_id}/reject", response_model=TransactionOut)
def reject_transaction(
tx_id: int,
db: Session = Depends(get_db),
admin=Depends(require_admin),
):
tx = db.get(Transaction, tx_id)
if not tx:
raise HTTPException(404, "Transaktion nicht gefunden")
if tx.status != "waiting":
raise HTTPException(status_code=409, detail=f"Transaktion bereits {tx.status}")
tx.status = "rejected"
tx.processed_at = datetime.now(timezone.utc)
tx.processed_by_id = admin.id
db.commit()
db.refresh(tx)
return tx