Subir archivos a "/"
This commit is contained in:
395
clientes.py
Normal file
395
clientes.py
Normal file
@@ -0,0 +1,395 @@
|
||||
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, LimiteTimbre, Usuario
|
||||
from app.models.enums import TipoMovimiento, TipoLimite
|
||||
from app.schemas import (
|
||||
ClienteCreate, ClienteUpdate, ClienteResponse, ClienteWithStats,
|
||||
BalanceResponse, RecargaTimbreCreate, MessageResponse, LimiteHistorialResponse, LimiteResponse
|
||||
)
|
||||
from app.dependencies import get_current_user
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("/", response_model=ClienteResponse, status_code=status.HTTP_201_CREATED)
|
||||
def crear_cliente(
|
||||
cliente: ClienteCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: Usuario = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
Crear un nuevo cliente con su límite de timbres inicial
|
||||
"""
|
||||
# Verificar si el email ya existe
|
||||
if db.query(Cliente).filter(Cliente.email == cliente.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 cliente.rfc and db.query(Cliente).filter(Cliente.rfc == cliente.rfc).first():
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="El RFC ya está registrado"
|
||||
)
|
||||
|
||||
db_cliente = Cliente(
|
||||
nombre=cliente.nombre,
|
||||
email=cliente.email,
|
||||
rfc=cliente.rfc,
|
||||
telefono=cliente.telefono,
|
||||
permite_subclientes=cliente.permite_subclientes
|
||||
)
|
||||
|
||||
db.add(db_cliente)
|
||||
db.flush() # Para obtener el ID sin hacer commit
|
||||
|
||||
# Crear el límite inicial si se proporciona
|
||||
if cliente.limite_timbres > 0:
|
||||
limite_inicial = LimiteTimbre(
|
||||
cliente_id=db_cliente.id,
|
||||
tipo=TipoLimite.ASIGNACION_INICIAL,
|
||||
cantidad=cliente.limite_timbres,
|
||||
descripcion="Límite inicial al crear el cliente"
|
||||
)
|
||||
db.add(limite_inicial)
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_cliente)
|
||||
|
||||
# Agregar campos calculados
|
||||
response = ClienteResponse.model_validate(db_cliente)
|
||||
response.limite_timbres = db_cliente.get_limite_actual(db)
|
||||
response.timbres_disponibles = db_cliente.get_timbres_disponibles(db)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@router.get("/", response_model=List[ClienteResponse])
|
||||
def listar_clientes(
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: Usuario = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
Listar todos los clientes
|
||||
"""
|
||||
clientes = db.query(Cliente).offset(skip).limit(limit).all()
|
||||
|
||||
# Agregar campos calculados a cada cliente
|
||||
response = []
|
||||
for cliente in clientes:
|
||||
cliente_dict = ClienteResponse.model_validate(cliente)
|
||||
cliente_dict.limite_timbres = cliente.get_limite_actual(db)
|
||||
cliente_dict.timbres_disponibles = cliente.get_timbres_disponibles(db)
|
||||
response.append(cliente_dict)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@router.get("/{cliente_id}", response_model=ClienteWithStats)
|
||||
def obtener_cliente(
|
||||
cliente_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: Usuario = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
Obtener un cliente por ID con estadísticas de subclientes
|
||||
"""
|
||||
cliente = db.query(Cliente).filter(Cliente.id == cliente_id).first()
|
||||
|
||||
if not cliente:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Cliente no encontrado"
|
||||
)
|
||||
|
||||
# Obtener estadísticas de subclientes
|
||||
total_subclientes = db.query(SubCliente).filter(
|
||||
SubCliente.cliente_id == cliente_id
|
||||
).count()
|
||||
|
||||
timbres_consumidos_subclientes = db.query(SubCliente).filter(
|
||||
SubCliente.cliente_id == cliente_id
|
||||
).with_entities(SubCliente.timbres_consumidos).all()
|
||||
|
||||
total_consumidos_sub = sum([t[0] for t in timbres_consumidos_subclientes])
|
||||
|
||||
# Obtener total de límites asignados
|
||||
from sqlalchemy import func as sql_func
|
||||
total_limites = db.query(sql_func.sum(LimiteTimbre.cantidad)).filter(
|
||||
LimiteTimbre.cliente_id == cliente_id
|
||||
).scalar() or 0
|
||||
|
||||
# Crear respuesta con estadísticas
|
||||
response = ClienteWithStats.model_validate(cliente)
|
||||
response.limite_timbres = cliente.get_limite_actual(db)
|
||||
response.timbres_disponibles = cliente.get_timbres_disponibles(db)
|
||||
response.total_subclientes = total_subclientes
|
||||
response.timbres_consumidos_subclientes = total_consumidos_sub
|
||||
response.total_limites_asignados = total_limites
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@router.put("/{cliente_id}", response_model=ClienteResponse)
|
||||
def actualizar_cliente(
|
||||
cliente_id: int,
|
||||
cliente_update: ClienteUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: Usuario = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
Actualizar información de un cliente
|
||||
"""
|
||||
db_cliente = db.query(Cliente).filter(Cliente.id == cliente_id).first()
|
||||
|
||||
if not db_cliente:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Cliente no encontrado"
|
||||
)
|
||||
|
||||
update_data = cliente_update.model_dump(exclude_unset=True)
|
||||
|
||||
# Verificar email único si se actualiza
|
||||
if "email" in update_data and update_data["email"] != db_cliente.email:
|
||||
if db.query(Cliente).filter(Cliente.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_cliente.rfc:
|
||||
if db.query(Cliente).filter(Cliente.rfc == update_data["rfc"]).first():
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="El RFC ya está registrado"
|
||||
)
|
||||
|
||||
# Actualizar campos básicos (excluyendo limite_timbres que ya no existe)
|
||||
update_data_without_limite = {k: v for k, v in update_data.items() if k != 'limite_timbres'}
|
||||
|
||||
for field, value in update_data_without_limite.items():
|
||||
setattr(db_cliente, field, value)
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_cliente)
|
||||
|
||||
# Preparar respuesta con campos calculados
|
||||
response = ClienteResponse.model_validate(db_cliente)
|
||||
response.limite_timbres = db_cliente.get_limite_actual(db)
|
||||
response.timbres_disponibles = db_cliente.get_timbres_disponibles(db)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@router.delete("/{cliente_id}", response_model=MessageResponse)
|
||||
def eliminar_cliente(
|
||||
cliente_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: Usuario = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
Eliminar un cliente (también elimina sus subclientes y movimientos)
|
||||
"""
|
||||
db_cliente = db.query(Cliente).filter(Cliente.id == cliente_id).first()
|
||||
|
||||
if not db_cliente:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Cliente no encontrado"
|
||||
)
|
||||
|
||||
db.delete(db_cliente)
|
||||
db.commit()
|
||||
|
||||
return {"message": f"Cliente {db_cliente.nombre} eliminado exitosamente"}
|
||||
|
||||
#==========================================================================================
|
||||
@router.get("/{cliente_id}/balance", response_model=BalanceResponse)
|
||||
def obtener_balance(
|
||||
cliente_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: Usuario = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
Obtener el balance de timbres de un cliente
|
||||
"""
|
||||
cliente = db.query(Cliente).filter(Cliente.id == cliente_id).first()
|
||||
|
||||
if not cliente:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Cliente no encontrado"
|
||||
)
|
||||
|
||||
total_subclientes = db.query(SubCliente).filter(
|
||||
SubCliente.cliente_id == cliente_id
|
||||
).count()
|
||||
|
||||
timbres_consumidos_subclientes = sum([
|
||||
s.timbres_consumidos for s in db.query(SubCliente).filter(
|
||||
SubCliente.cliente_id == cliente_id
|
||||
).all()
|
||||
])
|
||||
|
||||
limite_actual = cliente.get_limite_actual(db)
|
||||
timbres_disponibles = cliente.get_timbres_disponibles(db)
|
||||
porcentaje = (cliente.timbres_consumidos / limite_actual * 100) if limite_actual > 0 else 0
|
||||
|
||||
return {
|
||||
"cliente_id": cliente.id,
|
||||
"nombre_cliente": cliente.nombre,
|
||||
"limite_timbres": limite_actual,
|
||||
"timbres_disponibles": timbres_disponibles,
|
||||
"timbres_consumidos": cliente.timbres_consumidos,
|
||||
"porcentaje_uso": round(porcentaje, 2),
|
||||
"total_subclientes": total_subclientes,
|
||||
"timbres_consumidos_subclientes": timbres_consumidos_subclientes
|
||||
}
|
||||
|
||||
|
||||
|
||||
#==========================================================================================
|
||||
|
||||
@router.post("/{cliente_id}/recargar", response_model=MessageResponse)
|
||||
def recargar_timbres(
|
||||
cliente_id: int,
|
||||
recarga: RecargaTimbreCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: Usuario = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
Recargar timbres para un cliente (incrementa límite y disponibles)
|
||||
"""
|
||||
cliente = db.query(Cliente).filter(Cliente.id == cliente_id).first()
|
||||
|
||||
if not cliente:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Cliente no encontrado"
|
||||
)
|
||||
|
||||
# Crear nuevo límite (incremento)
|
||||
nuevo_limite = LimiteTimbre(
|
||||
cliente_id=cliente_id,
|
||||
tipo=TipoLimite.INCREMENTO,
|
||||
cantidad=recarga.cantidad,
|
||||
descripcion=recarga.descripcion or f"Recarga de {recarga.cantidad} timbres"
|
||||
)
|
||||
|
||||
db.add(nuevo_limite)
|
||||
db.flush()
|
||||
|
||||
# Registrar movimiento de recarga
|
||||
limite_actual = cliente.get_limite_actual(db)
|
||||
timbres_disponibles = cliente.get_timbres_disponibles(db)
|
||||
|
||||
movimiento = MovimientoTimbre(
|
||||
cliente_id=cliente_id,
|
||||
tipo=TipoMovimiento.RECARGA,
|
||||
cantidad=recarga.cantidad,
|
||||
descripcion=recarga.descripcion,
|
||||
balance_cliente=timbres_disponibles
|
||||
)
|
||||
|
||||
db.add(movimiento)
|
||||
db.commit()
|
||||
|
||||
return {
|
||||
"message": f"Se recargaron {recarga.cantidad} timbres exitosamente",
|
||||
"detail": {
|
||||
"nuevo_limite": limite_actual,
|
||||
"timbres_disponibles": timbres_disponibles
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@router.get("/{cliente_id}/limites", response_model=LimiteHistorialResponse)
|
||||
def obtener_historial_limites(
|
||||
cliente_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: Usuario = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
Obtener el historial completo de límites de un cliente
|
||||
"""
|
||||
cliente = db.query(Cliente).filter(Cliente.id == cliente_id).first()
|
||||
|
||||
if not cliente:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Cliente no encontrado"
|
||||
)
|
||||
|
||||
# Obtener todos los límites
|
||||
limites = db.query(LimiteTimbre).filter(
|
||||
LimiteTimbre.cliente_id == cliente_id
|
||||
).order_by(LimiteTimbre.created_at.desc()).all()
|
||||
|
||||
# Calcular totales
|
||||
from sqlalchemy import func as sql_func
|
||||
limite_actual = db.query(sql_func.sum(LimiteTimbre.cantidad)).filter(
|
||||
LimiteTimbre.cliente_id == cliente_id,
|
||||
LimiteTimbre.activo == 1
|
||||
).scalar() or 0
|
||||
|
||||
total_asignado = db.query(sql_func.sum(LimiteTimbre.cantidad)).filter(
|
||||
LimiteTimbre.cliente_id == cliente_id
|
||||
).scalar() or 0
|
||||
|
||||
return {
|
||||
"limite_actual": limite_actual,
|
||||
"total_asignado": total_asignado,
|
||||
"historial": limites
|
||||
}
|
||||
|
||||
|
||||
@router.post("/{cliente_id}/ajustar-limite", response_model=MessageResponse)
|
||||
def ajustar_limite(
|
||||
cliente_id: int,
|
||||
cantidad: int,
|
||||
descripcion: str = None,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: Usuario = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
Ajustar el límite de un cliente (puede ser positivo o negativo)
|
||||
"""
|
||||
cliente = db.query(Cliente).filter(Cliente.id == cliente_id).first()
|
||||
|
||||
if not cliente:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Cliente no encontrado"
|
||||
)
|
||||
|
||||
tipo = TipoLimite.INCREMENTO if cantidad > 0 else TipoLimite.DECREMENTO
|
||||
|
||||
nuevo_limite = LimiteTimbre(
|
||||
cliente_id=cliente_id,
|
||||
tipo=tipo,
|
||||
cantidad=abs(cantidad),
|
||||
descripcion=descripcion or f"Ajuste de límite: {cantidad}"
|
||||
)
|
||||
|
||||
db.add(nuevo_limite)
|
||||
db.commit()
|
||||
|
||||
limite_actual = cliente.get_limite_actual(db)
|
||||
timbres_disponibles = cliente.get_timbres_disponibles(db)
|
||||
|
||||
return {
|
||||
"message": "Límite ajustado exitosamente",
|
||||
"detail": {
|
||||
"ajuste": cantidad,
|
||||
"nuevo_limite": limite_actual,
|
||||
"timbres_disponibles": timbres_disponibles
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user