247 lines
8.5 KiB
Python
247 lines
8.5 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from prisma import Prisma
|
|
from typing import List, Optional
|
|
from datetime import datetime, timedelta
|
|
from app.models.pedido_cliente import (
|
|
PedidoClienteCreate, PedidoClienteUpdate, PedidoClienteResponse,
|
|
ReferenciaPedidoClienteUpdate
|
|
)
|
|
from app.api.dependencies import get_prisma
|
|
|
|
router = APIRouter(prefix="/pedidos-cliente", tags=["pedidos-cliente"])
|
|
|
|
|
|
def calcular_estado_referencia(unidades_solicitadas: int, unidades_en_stock: int) -> str:
|
|
"""Calcula el estado de una referencia"""
|
|
unidades_pendientes = max(0, unidades_solicitadas - unidades_en_stock)
|
|
if unidades_pendientes <= 0:
|
|
return "completo"
|
|
elif unidades_pendientes < unidades_solicitadas:
|
|
return "parcial"
|
|
return "pendiente"
|
|
|
|
|
|
def es_urgente(fecha_cita: Optional[datetime]) -> bool:
|
|
"""Verifica si un pedido es urgente (menos de 12 horas)"""
|
|
if not fecha_cita:
|
|
return False
|
|
ahora = datetime.now(fecha_cita.tzinfo) if fecha_cita.tzinfo else datetime.now()
|
|
tiempo_restante = fecha_cita - ahora
|
|
return 0 < tiempo_restante.total_seconds() < 12 * 3600
|
|
|
|
|
|
@router.get("/", response_model=List[PedidoClienteResponse])
|
|
async def listar_pedidos(
|
|
skip: int = 0,
|
|
limit: int = 100,
|
|
estado: Optional[str] = None,
|
|
urgente: Optional[bool] = None,
|
|
matricula: Optional[str] = None,
|
|
db: Prisma = Depends(get_prisma)
|
|
):
|
|
"""Listar pedidos de cliente"""
|
|
where = {}
|
|
if estado:
|
|
where["estado"] = estado
|
|
if matricula:
|
|
where["cliente"] = {"matriculaVehiculo": {"contains": matricula, "mode": "insensitive"}}
|
|
|
|
pedidos = await db.pedidocliente.find_many(
|
|
where=where,
|
|
skip=skip,
|
|
take=limit,
|
|
include={
|
|
"cliente": True,
|
|
"referencias": True
|
|
},
|
|
order_by={"fechaPedido": "desc"}
|
|
)
|
|
|
|
# Filtrar por urgente si se solicita
|
|
if urgente is not None:
|
|
pedidos = [p for p in pedidos if es_urgente(p.fechaCita) == urgente]
|
|
|
|
# Agregar es_urgente a cada pedido
|
|
result = []
|
|
for pedido in pedidos:
|
|
pedido_dict = pedido.dict()
|
|
pedido_dict["es_urgente"] = es_urgente(pedido.fechaCita)
|
|
result.append(pedido_dict)
|
|
|
|
return result
|
|
|
|
|
|
@router.get("/{pedido_id}", response_model=PedidoClienteResponse)
|
|
async def obtener_pedido(
|
|
pedido_id: int,
|
|
db: Prisma = Depends(get_prisma)
|
|
):
|
|
"""Obtener un pedido por ID"""
|
|
pedido = await db.pedidocliente.find_unique(
|
|
where={"id": pedido_id},
|
|
include={"cliente": True, "referencias": True}
|
|
)
|
|
if not pedido:
|
|
raise HTTPException(status_code=404, detail="Pedido no encontrado")
|
|
|
|
pedido_dict = pedido.dict()
|
|
pedido_dict["es_urgente"] = es_urgente(pedido.fechaCita)
|
|
return pedido_dict
|
|
|
|
|
|
@router.post("/", response_model=PedidoClienteResponse, status_code=201)
|
|
async def crear_pedido(
|
|
pedido: PedidoClienteCreate,
|
|
db: Prisma = Depends(get_prisma)
|
|
):
|
|
"""Crear un nuevo pedido de cliente"""
|
|
try:
|
|
# Verificar que el cliente existe
|
|
cliente = await db.cliente.find_unique(where={"id": pedido.cliente_id})
|
|
if not cliente:
|
|
raise HTTPException(status_code=404, detail="Cliente no encontrado")
|
|
|
|
# Crear pedido
|
|
referencias_data = pedido.referencias or []
|
|
pedido_data = pedido.dict(exclude={"referencias"})
|
|
# Convertir snake_case a camelCase para Prisma
|
|
pedido_data_prisma = {
|
|
"numeroPedido": pedido_data["numero_pedido"],
|
|
"clienteId": pedido_data["cliente_id"],
|
|
"fechaCita": pedido_data.get("fecha_cita"),
|
|
"estado": pedido_data["estado"],
|
|
"presupuestoId": pedido_data.get("presupuesto_id"),
|
|
"archivoPdfPath": pedido_data.get("archivo_pdf_path"),
|
|
"referencias": {
|
|
"create": [
|
|
{
|
|
"referencia": ref.referencia,
|
|
"denominacion": ref.denominacion,
|
|
"unidadesSolicitadas": ref.unidades_solicitadas,
|
|
"unidadesEnStock": ref.unidades_en_stock,
|
|
"unidadesPendientes": max(0, ref.unidades_solicitadas - ref.unidades_en_stock),
|
|
"estado": calcular_estado_referencia(ref.unidades_solicitadas, ref.unidades_en_stock)
|
|
}
|
|
for ref in referencias_data
|
|
]
|
|
}
|
|
}
|
|
|
|
nuevo_pedido = await db.pedidocliente.create(
|
|
data=pedido_data_prisma,
|
|
include={"cliente": True, "referencias": True}
|
|
)
|
|
|
|
nuevo_pedido_dict = nuevo_pedido.dict()
|
|
nuevo_pedido_dict["es_urgente"] = es_urgente(nuevo_pedido.fechaCita)
|
|
return nuevo_pedido_dict
|
|
except Exception as e:
|
|
raise HTTPException(status_code=400, detail=str(e))
|
|
|
|
|
|
@router.put("/{pedido_id}", response_model=PedidoClienteResponse)
|
|
async def actualizar_pedido(
|
|
pedido_id: int,
|
|
pedido: PedidoClienteUpdate,
|
|
db: Prisma = Depends(get_prisma)
|
|
):
|
|
"""Actualizar un pedido"""
|
|
pedido_existente = await db.pedidocliente.find_unique(where={"id": pedido_id})
|
|
if not pedido_existente:
|
|
raise HTTPException(status_code=404, detail="Pedido no encontrado")
|
|
|
|
data = {k: v for k, v in pedido.dict().items() if v is not None}
|
|
# Convertir snake_case a camelCase
|
|
data_prisma = {}
|
|
field_mapping = {
|
|
"numero_pedido": "numeroPedido",
|
|
"cliente_id": "clienteId",
|
|
"fecha_cita": "fechaCita",
|
|
"presupuesto_id": "presupuestoId",
|
|
"archivo_pdf_path": "archivoPdfPath"
|
|
}
|
|
for key, value in data.items():
|
|
prisma_key = field_mapping.get(key, key)
|
|
data_prisma[prisma_key] = value
|
|
|
|
pedido_actualizado = await db.pedidocliente.update(
|
|
where={"id": pedido_id},
|
|
data=data_prisma,
|
|
include={"cliente": True, "referencias": True}
|
|
)
|
|
|
|
pedido_dict = pedido_actualizado.dict()
|
|
pedido_dict["es_urgente"] = es_urgente(pedido_actualizado.fechaCita)
|
|
return pedido_dict
|
|
|
|
|
|
@router.post("/{pedido_id}/actualizar-estado")
|
|
async def actualizar_estado_pedido(
|
|
pedido_id: int,
|
|
estado: str,
|
|
db: Prisma = Depends(get_prisma)
|
|
):
|
|
"""Actualizar el estado de un pedido"""
|
|
estados_validos = ["pendiente_revision", "en_revision", "pendiente_materiales", "completado"]
|
|
if estado not in estados_validos:
|
|
raise HTTPException(status_code=400, detail="Estado inválido")
|
|
|
|
pedido = await db.pedidocliente.find_unique(where={"id": pedido_id})
|
|
if not pedido:
|
|
raise HTTPException(status_code=404, detail="Pedido no encontrado")
|
|
|
|
await db.pedidocliente.update(
|
|
where={"id": pedido_id},
|
|
data={"estado": estado}
|
|
)
|
|
return {"status": "Estado actualizado"}
|
|
|
|
|
|
@router.post("/referencias/{referencia_id}/marcar-stock")
|
|
async def marcar_stock_referencia(
|
|
referencia_id: int,
|
|
unidades_en_stock: int,
|
|
db: Prisma = Depends(get_prisma)
|
|
):
|
|
"""Marcar unidades en stock para una referencia"""
|
|
referencia = await db.referenciapedidocliente.find_unique(
|
|
where={"id": referencia_id},
|
|
include={"pedidoCliente": True}
|
|
)
|
|
if not referencia:
|
|
raise HTTPException(status_code=404, detail="Referencia no encontrada")
|
|
|
|
unidades_en_stock = max(0, unidades_en_stock)
|
|
unidades_pendientes = max(0, referencia.unidadesSolicitadas - unidades_en_stock)
|
|
estado = calcular_estado_referencia(referencia.unidadesSolicitadas, unidades_en_stock)
|
|
|
|
referencia_actualizada = await db.referenciapedidocliente.update(
|
|
where={"id": referencia_id},
|
|
data={
|
|
"unidadesEnStock": unidades_en_stock,
|
|
"unidadesPendientes": unidades_pendientes,
|
|
"estado": estado
|
|
}
|
|
)
|
|
|
|
# Verificar si todas las referencias están completas
|
|
todas_referencias = await db.referenciapedidocliente.find_many(
|
|
where={"pedidoClienteId": referencia.pedidoClienteId}
|
|
)
|
|
|
|
todas_completas = all(ref.unidadesPendientes == 0 for ref in todas_referencias)
|
|
|
|
if todas_completas and referencia.pedidoCliente.estado != "completado":
|
|
await db.pedidocliente.update(
|
|
where={"id": referencia.pedidoClienteId},
|
|
data={"estado": "completado"}
|
|
)
|
|
elif referencia.pedidoCliente.estado == "pendiente_revision":
|
|
await db.pedidocliente.update(
|
|
where={"id": referencia.pedidoClienteId},
|
|
data={"estado": "en_revision"}
|
|
)
|
|
|
|
return referencia_actualizada
|
|
|