from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from typing import List from app.database import get_db from app.models import Cliente, SubCliente, MovimientoTimbre, Usuario from app.models.enums import TipoMovimiento, EstadoCliente from app.schemas import ( SubClienteCreate, SubClienteUpdate, SubClienteResponse, ConsumoTimbreCreate, MessageResponse ) from app.dependencies import get_current_user router = APIRouter() @router.post("/", response_model=SubClienteResponse, status_code=status.HTTP_201_CREATED) def crear_subcliente( subcliente: SubClienteCreate, db: Session = Depends(get_db), current_user: Usuario = Depends(get_current_user) ): """ Crear un nuevo subcliente asociado a un cliente """ # Verificar que el cliente existe cliente = db.query(Cliente).filter(Cliente.id == subcliente.cliente_id).first() if not cliente: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Cliente no encontrado" ) # Verificar que el cliente permite subclientes if not cliente.permite_subclientes: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Este cliente no permite crear subclientes" ) # Verificar si el email ya existe if db.query(SubCliente).filter(SubCliente.email == subcliente.email).first(): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="El email ya está registrado" ) # Verificar si el RFC ya existe (si se proporciona) if subcliente.rfc and db.query(SubCliente).filter(SubCliente.rfc == subcliente.rfc).first(): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="El RFC ya está registrado" ) db_subcliente = SubCliente(**subcliente.model_dump()) db.add(db_subcliente) db.commit() db.refresh(db_subcliente) return db_subcliente @router.get("/", response_model=List[SubClienteResponse]) def listar_subclientes( cliente_id: int = None, skip: int = 0, limit: int = 100, db: Session = Depends(get_db), current_user: Usuario = Depends(get_current_user) ): """ Listar subclientes, opcionalmente filtrados por cliente_id """ query = db.query(SubCliente) if cliente_id: query = query.filter(SubCliente.cliente_id == cliente_id) subclientes = query.offset(skip).limit(limit).all() return subclientes @router.get("/{subcliente_id}", response_model=SubClienteResponse) def obtener_subcliente( subcliente_id: int, db: Session = Depends(get_db), current_user: Usuario = Depends(get_current_user) ): """ Obtener un subcliente por ID """ subcliente = db.query(SubCliente).filter(SubCliente.id == subcliente_id).first() if not subcliente: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="SubCliente no encontrado" ) return subcliente @router.put("/{subcliente_id}", response_model=SubClienteResponse) def actualizar_subcliente( subcliente_id: int, subcliente_update: SubClienteUpdate, db: Session = Depends(get_db), current_user: Usuario = Depends(get_current_user) ): """ Actualizar información de un subcliente """ db_subcliente = db.query(SubCliente).filter(SubCliente.id == subcliente_id).first() if not db_subcliente: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="SubCliente no encontrado" ) update_data = subcliente_update.model_dump(exclude_unset=True) # Verificar email único si se actualiza if "email" in update_data and update_data["email"] != db_subcliente.email: if db.query(SubCliente).filter(SubCliente.email == update_data["email"]).first(): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="El email ya está registrado" ) # Verificar RFC único si se actualiza if "rfc" in update_data and update_data["rfc"] != db_subcliente.rfc: if db.query(SubCliente).filter(SubCliente.rfc == update_data["rfc"]).first(): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="El RFC ya está registrado" ) for field, value in update_data.items(): setattr(db_subcliente, field, value) db.commit() db.refresh(db_subcliente) return db_subcliente @router.delete("/{subcliente_id}", response_model=MessageResponse) def eliminar_subcliente( subcliente_id: int, db: Session = Depends(get_db), current_user: Usuario = Depends(get_current_user) ): """ Eliminar un subcliente """ db_subcliente = db.query(SubCliente).filter(SubCliente.id == subcliente_id).first() if not db_subcliente: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="SubCliente no encontrado" ) db.delete(db_subcliente) db.commit() return {"message": f"SubCliente {db_subcliente.nombre} eliminado exitosamente"} @router.post("/{subcliente_id}/consumir", response_model=MessageResponse) def consumir_timbre( subcliente_id: int, consumo: ConsumoTimbreCreate, db: Session = Depends(get_db), current_user: Usuario = Depends(get_current_user) ): """ Consumir timbres para un subcliente (descuenta del cliente padre) """ subcliente = db.query(SubCliente).filter(SubCliente.id == subcliente_id).first() if not subcliente: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="SubCliente no encontrado" ) # Verificar que el subcliente esté activo if subcliente.estado != EstadoCliente.ACTIVO: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"El subcliente está {subcliente.estado}" ) cliente = db.query(Cliente).filter(Cliente.id == subcliente.cliente_id).first() # Verificar que el cliente esté activo if cliente.estado != EstadoCliente.ACTIVO: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"El cliente está {cliente.estado}" ) # Obtener timbres disponibles calculados timbres_disponibles = cliente.get_timbres_disponibles(db) # Verificar que hay timbres disponibles en el cliente if timbres_disponibles < consumo.cantidad: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"Timbres insuficientes. Disponibles: {timbres_disponibles}, Solicitados: {consumo.cantidad}" ) # Verificar límite propio del subcliente si existe if subcliente.limite_propio is not None: timbres_disponibles_subcliente = subcliente.limite_propio - subcliente.timbres_consumidos if timbres_disponibles_subcliente < consumo.cantidad: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"Límite del subcliente excedido. Disponibles: {timbres_disponibles_subcliente}" ) # Realizar el consumo cliente.timbres_consumidos += consumo.cantidad subcliente.timbres_consumidos += consumo.cantidad # Calcular nuevo balance nuevo_balance = cliente.get_timbres_disponibles(db) # Registrar movimiento movimiento = MovimientoTimbre( cliente_id=cliente.id, subcliente_id=subcliente.id, tipo=TipoMovimiento.CONSUMO, cantidad=consumo.cantidad, descripcion=consumo.descripcion, referencia_externa=consumo.referencia_externa, balance_cliente=nuevo_balance ) db.add(movimiento) db.commit() return { "message": f"Se consumieron {consumo.cantidad} timbre(s) exitosamente", "detail": { "subcliente": subcliente.nombre, "timbres_consumidos_subcliente": subcliente.timbres_consumidos, "timbres_disponibles_cliente": nuevo_balance } }