from fastapi import APIRouter, Depends, HTTPException from sqlalchemy import select from sqlalchemy.orm import Session from ocpp.v201.call import RequestStopTransaction from app.ocpp_proto import chargepoint_manager from app.schemas.auth_token import AccessToken from app.database import get_db from app.schemas.meter_value import MeterValue from app.schemas.transaction import Transaction, RemoteTransactionStartStopResponse, TransactionStatus, RemoteTransactionStartStopStatus from app.models.transaction import Transaction as DbTransaction from app.models.meter_value import MeterValue as DbMeterValue from app.schemas.user import Role from app.security.jwt_bearer import JWTBearer router = APIRouter( prefix="/transactions", tags=["Transaction (v1)"] ) @router.get(path="", response_model=list[Transaction]) async def get_transactions( skip: int = 0, limit: int = 20, db: Session = Depends(get_db), token: AccessToken = Depends(JWTBearer()), ): stmt = select(DbTransaction) if (token.role != Role.ADMINISTRATOR): stmt = stmt.where(DbTransaction.user_id == token.subject) stmt = stmt.order_by(DbTransaction.started_at).offset(skip).limit(limit) result = db.execute(stmt) return result.scalars().all() @router.get(path="/{transaction_id}", response_model=Transaction) async def get_transaction( transaction_id: str, db: Session = Depends(get_db), token: AccessToken = Depends(JWTBearer()), ): stmt = select(DbTransaction).where(DbTransaction.id == transaction_id) result = db.execute(stmt) transaction = result.scalars().first() if transaction == None: raise HTTPException(404, "Transaction not found") if token.role != Role.ADMINISTRATOR and transaction.user_id != token.subject: raise HTTPException(404, "Transaction not found") return transaction @router.get(path="/{transaction_id}/meter-values", response_model=list[MeterValue]) async def get_transaction_meter_values( transaction_id: str, db: Session = Depends(get_db), token: AccessToken = Depends(JWTBearer()), ): stmt = select(DbTransaction).where(DbTransaction.id == transaction_id) result = db.execute(stmt) transaction = result.scalars().first() if transaction == None: raise HTTPException(404, "Transaction not found") if token.role != Role.ADMINISTRATOR and transaction.user_id != token.subject: raise HTTPException(404, "Transaction not found") stmt = select(DbMeterValue).where(DbMeterValue.transaction_id == transaction_id).order_by(DbMeterValue.timestamp) result = db.execute(stmt) return result.scalars().all() @router.post(path="/{transaction_id}/remote-stop", response_model=RemoteTransactionStartStopResponse) async def remote_stop_transaction( transaction_id: str, db: Session = Depends(get_db), token: AccessToken = Depends(JWTBearer()), ): stmt = select(DbTransaction).where(DbTransaction.id == transaction_id) result = db.execute(stmt) transaction = result.scalars().first() if transaction == None: raise HTTPException(404, "Transaction not found") if token.role != Role.ADMINISTRATOR and transaction.user_id != token.subject: raise HTTPException(404, "Transaction not found") if transaction.status != TransactionStatus.ONGOING: raise HTTPException(status_code=422, detail=[{ "loc": ["path", "transaction_id"], "msg": "Transaction is not ongoing", "type": "invalid_transaction_state" }]) if chargepoint_manager.is_connected(transaction.chargepoint_id) == False: raise HTTPException(status_code=503, detail="chargepoint_offline") try: result = await chargepoint_manager.call( transaction.chargepoint_id, payload=RequestStopTransaction( transaction_id=transaction.id ) ) if RemoteTransactionStartStopStatus(result.status) != RemoteTransactionStartStopStatus.REJECTED: raise HTTPException(status_code=500, detail=result.status) return RemoteTransactionStartStopResponse(status=result.status) except TimeoutError: raise HTTPException(status_code=503, detail="chargepoint_operation_timeout")