Actualizar dominios a dashboardapi y dashboardv1
This commit is contained in:
571
backend/main.py
571
backend/main.py
@@ -1,483 +1,290 @@
|
||||
# backend/main.py
|
||||
"""
|
||||
API REST del Dashboard — FastAPI.
|
||||
Reutiliza la lógica de negocio existente en modules/ y core/.
|
||||
Cada endpoint devuelve JSON; el frontend (React) lo consume.
|
||||
"""
|
||||
from fastapi import FastAPI, Query, HTTPException
|
||||
"""API REST del Dashboard de LEADS (FastAPI)."""
|
||||
from fastapi import FastAPI, Query, HTTPException, Body
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from datetime import datetime
|
||||
import uvicorn
|
||||
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Cargar variables de entorno desde .env
|
||||
load_dotenv()
|
||||
|
||||
import services
|
||||
from cache_manager import start_background_refresh, cache_invalidate, cache_stats
|
||||
from cache_manager import start_background_refresh, cache_stats
|
||||
|
||||
app = FastAPI(title="Dashboard API", version="1.0")
|
||||
app = FastAPI(title="Dashboard Leads API", version="1.0")
|
||||
|
||||
|
||||
# Orígenes CORS configurables
|
||||
cors_env = os.getenv("CORS_ORIGINS", "*")
|
||||
origins = [o.strip() for o in cors_env.split(",") if o.strip() and o.strip() != "*"]
|
||||
|
||||
# CORS — permite que el frontend React (otro puerto) consuma la API
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"], # en red local está bien; en producción restringir
|
||||
allow_origins=origins if origins else ["*"],
|
||||
allow_origin_regex=".*",
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
# ── Arranque: precarga + refresco en segundo plano ─────────────────────────
|
||||
@app.on_event("startup")
|
||||
def _startup():
|
||||
print("[startup] Precargando datos iniciales...")
|
||||
try:
|
||||
services.precargar_todo()
|
||||
print("[startup] Precarga completa.")
|
||||
except Exception as e:
|
||||
print(f"[startup] Precarga falló: {e}")
|
||||
# Refresco cada 4 min en segundo plano (TTL del caché es 5 min)
|
||||
# Precarga en SEGUNDO PLANO (hilo aparte) para no bloquear el arranque del server.
|
||||
import threading
|
||||
def _precarga_bg():
|
||||
print("[startup] Precargando datos de Leads (2do plano)...")
|
||||
try:
|
||||
services.precargar_todo()
|
||||
print("[startup] Precarga completa.")
|
||||
except Exception as e:
|
||||
print(f"[startup] Precarga falló: {e}")
|
||||
threading.Thread(target=_precarga_bg, daemon=True).start()
|
||||
# Otros General en un hilo aparte (sus consultas son mas pesadas)
|
||||
def _precarga_otros_bg():
|
||||
try:
|
||||
services.precargar_otros_general()
|
||||
except Exception as e:
|
||||
print(f"[startup] Precarga Otros falló: {e}")
|
||||
threading.Thread(target=_precarga_otros_bg, daemon=True).start()
|
||||
start_background_refresh(services.refrescar_todo, interval=900)
|
||||
|
||||
|
||||
# ── Salud / utilidades ─────────────────────────────────────────────────────
|
||||
@app.get("/api/health")
|
||||
def health():
|
||||
return {"status": "ok", "hora": datetime.now().isoformat()}
|
||||
|
||||
|
||||
@app.get("/api/cache/stats")
|
||||
def cache_estadisticas():
|
||||
def stats():
|
||||
return cache_stats()
|
||||
|
||||
|
||||
@app.post("/api/cache/refresh")
|
||||
def cache_refresh():
|
||||
def refresh():
|
||||
services.refrescar_todo()
|
||||
return {"status": "refrescado"}
|
||||
|
||||
|
||||
@app.post("/api/cache/refresh-manual")
|
||||
def cache_refresh_manual(clave: str = None):
|
||||
"""Botón 'Actualizar': re-lee las bases para la vista actual (clave) al instante,
|
||||
y recachea el resto en segundo plano."""
|
||||
return services.refrescar_manual(clave)
|
||||
@app.get("/api/leads/filtros")
|
||||
def get_filtros():
|
||||
try:
|
||||
return services.opciones_filtros()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/api/ultima-actualizacion")
|
||||
def api_ultima_actualizacion():
|
||||
return services.ultima_actualizacion()
|
||||
|
||||
|
||||
@app.post("/api/precarga/modulo")
|
||||
def api_precarga_modulo(modulo: str, ano: int):
|
||||
"""Calienta en 2º plano la tabla principal del módulo para el año indicado."""
|
||||
return services.precargar_modulo(modulo, ano)
|
||||
|
||||
|
||||
@app.get("/api/precarga/estado")
|
||||
def api_precarga_estado(modulo: str):
|
||||
"""Progreso de la precarga (para el aviso 'ya cargó todo')."""
|
||||
return services.precarga_estado(modulo)
|
||||
|
||||
|
||||
@app.get("/api/periodo-actual")
|
||||
def periodo_actual():
|
||||
dm = services.get_dm()
|
||||
return {"ano": dm.get_current_year(), "mes": dm.get_current_month()}
|
||||
|
||||
|
||||
# ── OCUPABILIDAD ───────────────────────────────────────────────────────────
|
||||
@app.get("/api/ocupabilidad")
|
||||
def get_ocupabilidad(
|
||||
ano: int = Query(...),
|
||||
mes: int = Query(...),
|
||||
sede: str = Query("TODOS"),
|
||||
@app.get("/api/leads")
|
||||
def get_leads(
|
||||
ano: str = Query("TODOS"),
|
||||
mes: str = Query("TODOS"),
|
||||
dia: str = Query("TODOS"),
|
||||
programa: str = Query("TODOS"),
|
||||
):
|
||||
try:
|
||||
return services.ocupabilidad(ano, mes, sede, programa)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/api/ocupabilidad/alumnos")
|
||||
def get_ocupabilidad_alumnos(num_indice: str = Query(...)):
|
||||
try:
|
||||
return services.ocupabilidad_alumnos(num_indice)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
# ── VENTAS ─────────────────────────────────────────────────────────────────
|
||||
@app.get("/api/ventas")
|
||||
def get_ventas(ano: int = Query(...), mes: int = Query(...),
|
||||
sede: str = Query("TODOS"), programa: str = Query("TODOS"),
|
||||
con_overrides: bool = Query(False)):
|
||||
try:
|
||||
return services.ventas(ano, mes, sede, programa, con_overrides)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/api/ventas/detalle")
|
||||
def get_ventas_detalle(
|
||||
vendedor: str = Query(...),
|
||||
ano: int = Query(...),
|
||||
mes: int = Query(...),
|
||||
tipo: str = Query("Venta Inscritos"),
|
||||
sede: str = Query("TODOS"),
|
||||
programa: str = Query("TODOS"),
|
||||
):
|
||||
try:
|
||||
return services.ventas_detalle(vendedor, ano, mes, tipo, sede, programa)
|
||||
return services.leads_dashboard(ano, mes, dia, programa, sede)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
# ── COBRANZA ───────────────────────────────────────────────────────────────
|
||||
@app.get("/api/cobranza")
|
||||
def get_cobranza(
|
||||
ano: int = Query(...),
|
||||
mes: int = Query(...),
|
||||
sectorista: str = Query("TODOS"),
|
||||
agrupacion: str = Query("SEDE"),
|
||||
sede_filtro: str = Query("TODOS"),
|
||||
prog_filtro: str = Query("TODOS"),
|
||||
venc_hasta: str = Query("TODOS"),
|
||||
@app.get("/api/vendedores")
|
||||
def get_vendedores(
|
||||
ano: str = Query("TODOS"),
|
||||
mes: str = Query("TODOS"),
|
||||
dia: str = Query("TODOS"),
|
||||
):
|
||||
try:
|
||||
return services.cobranza(ano, mes, sectorista, agrupacion, sede_filtro, prog_filtro, venc_hasta)
|
||||
return services.vendedores_dashboard(ano, mes, dia)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/api/cobranza/detalle")
|
||||
def get_cobranza_detalle(
|
||||
grupo: str = Query(...),
|
||||
ano: int = Query(...),
|
||||
mes: int = Query(...),
|
||||
sectorista: str = Query("TODOS"),
|
||||
agrupacion: str = Query("PROGRAMA"),
|
||||
prog_filtro: str = Query("TODOS"),
|
||||
sede_filtro: str = Query("TODOS"),
|
||||
venc_hasta: str = Query("TODOS"),
|
||||
@app.get("/api/roas")
|
||||
def get_roas(
|
||||
ano: str = Query("TODOS"),
|
||||
mes: str = Query("TODOS"),
|
||||
dia: str = Query("TODOS"),
|
||||
):
|
||||
try:
|
||||
return services.cobranza_detalle(grupo, ano, mes, sectorista, agrupacion, prog_filtro, sede_filtro, venc_hasta)
|
||||
return services.roas_dashboard(ano, mes, dia)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
# ── RENTABILIDAD ───────────────────────────────────────────────────────────
|
||||
@app.get("/api/rentabilidad")
|
||||
def get_rentabilidad(
|
||||
ano: int = Query(...),
|
||||
mes: int = Query(...),
|
||||
sede: str = Query("TODOS"),
|
||||
programa: str = Query("TODOS"),
|
||||
@app.get("/api/conjuntos-sin-sede")
|
||||
def get_conjuntos_sin_sede():
|
||||
try:
|
||||
return services.conjuntos_sin_sede()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/conjuntos-sin-sede/guardar")
|
||||
def post_conjunto_sede(body: dict = Body(...)):
|
||||
try:
|
||||
cambios = body.get("cambios") or []
|
||||
return services.guardar_conjunto_sede(cambios)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/conjuntos-sin-sede/borrar")
|
||||
def post_borrar_conjunto_sede(body: dict = Body(...)):
|
||||
try:
|
||||
return services.borrar_conjunto_sede(body.get("conjunto") or "")
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/api/otros-general")
|
||||
def get_otros_general(
|
||||
ano: str = Query("TODOS"),
|
||||
mes: str = Query("TODOS"),
|
||||
dia: str = Query("TODOS"),
|
||||
):
|
||||
try:
|
||||
return services.rentabilidad(ano, mes, sede, programa)
|
||||
return services.otros_general(ano, mes, dia)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/api/rentabilidad/detalle")
|
||||
def get_rentabilidad_detalle(
|
||||
programa: str = Query(...),
|
||||
ano: int = Query(...),
|
||||
mes: int = Query(...),
|
||||
):
|
||||
@app.get("/api/conjuntos-sin-pauta")
|
||||
def get_conjuntos_sin_pauta():
|
||||
try:
|
||||
return services.rentabilidad_detalle(programa, ano, mes)
|
||||
return {"conjuntos": services.conjuntos_sin_pauta()}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/api/rentabilidad/costos")
|
||||
def get_rentabilidad_costos(
|
||||
programa: str = Query(...),
|
||||
ano: int = Query(...),
|
||||
mes: int = Query(...),
|
||||
):
|
||||
@app.get("/api/programas-ocultos")
|
||||
def get_programas_ocultos():
|
||||
try:
|
||||
return services.rentabilidad_costos(programa, ano, mes)
|
||||
return services.programas_ocultos()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
# ── SALDO PENDIENTE ────────────────────────────────────────────────────────
|
||||
@app.get("/api/saldo-pendiente")
|
||||
def get_saldo_pendiente(tipo_cuota: str = Query("1° Cuota")):
|
||||
@app.post("/api/programas-ocultos/encender")
|
||||
def post_encender_programas(body: dict = Body(...)):
|
||||
try:
|
||||
return services.saldo_pendiente(tipo_cuota)
|
||||
nis = body.get("num_indices") or []
|
||||
return services.encender_programas(nis)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
# ── GUARDAR COSTOS ─────────────────────────────────────────────────────────
|
||||
from fastapi import Body
|
||||
|
||||
@app.post("/api/rentabilidad/costos/guardar")
|
||||
def post_guardar_costos(payload: dict = Body(...)):
|
||||
@app.get("/api/programas-disponibles")
|
||||
def get_programas_disponibles():
|
||||
try:
|
||||
num_indice = payload.get("num_indice", "")
|
||||
costos_inicial = payload.get("inicial", {})
|
||||
costos_actual = payload.get("actual", {})
|
||||
ok = services.guardar_costos(num_indice, costos_inicial, costos_actual)
|
||||
if ok:
|
||||
return {"status": "ok"}
|
||||
raise HTTPException(status_code=500, detail="No se pudo guardar (verifica Supabase)")
|
||||
return services.programas_disponibles()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/api/leyenda-anuncios")
|
||||
def get_leyenda():
|
||||
try:
|
||||
return services.leyenda_anuncios()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/leyenda-anuncios/guardar")
|
||||
def post_leyenda(body: dict = Body(...)):
|
||||
try:
|
||||
cambios = body.get("cambios") or []
|
||||
return services.guardar_leyenda(cambios)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
# ── LEYENDA (campanias de Supabase): listar / agregar / borrar ──
|
||||
@app.get("/api/campanias")
|
||||
def get_campanias():
|
||||
try:
|
||||
return {"filas": services.campanias_listar()}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/campanias/agregar")
|
||||
def post_campania(body: dict = Body(...)):
|
||||
try:
|
||||
return services.campania_agregar(body or {})
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/campanias/editar")
|
||||
def post_editar_campania(body: dict = Body(...)):
|
||||
try:
|
||||
cid = body.get("id")
|
||||
return services.campania_editar(cid, body or {})
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/campanias/borrar")
|
||||
def post_borrar_campania(body: dict = Body(...)):
|
||||
try:
|
||||
return services.campania_borrar(body.get("id"))
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/api/pauta/uso")
|
||||
def get_uso_pauta(pauta: str = Query(...), excluir: str = Query("")):
|
||||
try:
|
||||
return services.uso_de_pauta(pauta, excluir)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/curso/guardar-pauta")
|
||||
def post_guardar_pauta(body: dict = Body(...)):
|
||||
try:
|
||||
ni = str(body.get("num_indice", "")).strip()
|
||||
pa = str(body.get("pauta", "")).strip()
|
||||
cj = body.get("conjunto") or None
|
||||
contar = body.get("contar") # "SI" / "NO" / None
|
||||
if not ni:
|
||||
raise HTTPException(status_code=400, detail="num_indice es obligatorio")
|
||||
return services.guardar_edicion_curso(ni, pa, cj, contar)
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/cobranza/clasificar-sede")
|
||||
def post_clasificar_sede(payload: dict = Body(...)):
|
||||
@app.get("/api/ultima-actualizacion")
|
||||
def get_ultima_actualizacion():
|
||||
try:
|
||||
nombres = payload.get("programas", [])
|
||||
return {"mapa": services.clasificar_programas(nombres)}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.post("/api/cobranza/clasificar-full")
|
||||
def post_clasificar_full(payload: dict = Body(...)):
|
||||
"""Devuelve {programa: {sede, filtro}} usando el JSON REPLACE_META."""
|
||||
try:
|
||||
nombres = payload.get("programas", [])
|
||||
return {"mapa": services.clasificar_programas_full(nombres)}
|
||||
return services.ultima_actualizacion()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/api/debug/supabase-inicios")
|
||||
def debug_supabase_inicios(ano: int, mes: int):
|
||||
"""Diagnóstico: muestra qué hay en Supabase y por qué (no) se inyecta el rojo."""
|
||||
@app.get("/api/leads/alertas")
|
||||
def get_alertas():
|
||||
"""Valores de programa/sede en cartera_junta que NO están en el diccionario."""
|
||||
try:
|
||||
dm = services.get_dm()
|
||||
# 1) ¿Supabase conectado?
|
||||
conectado = dm.supabase_client is not None
|
||||
error_conexion = getattr(dm, "supabase_error", None)
|
||||
# 2) Fechas guardadas en Supabase
|
||||
fechas = dm.obtener_fechas_originales()
|
||||
# 3) Índices que SQL trae este mes
|
||||
crudos = dm.ejecutar_consulta_cursos(str(ano), str(mes))
|
||||
indices_sql = sorted({str(f.get("num_indice","")).strip() for f in (crudos or [])})
|
||||
# 4) Para cada fecha en Supabase, calcular su mes y si coincide con el filtrado
|
||||
analisis = []
|
||||
for idx, info in fechas.items():
|
||||
fecha_orig = info.get("fecha","") if isinstance(info, dict) else str(info)
|
||||
mes_orig = ""
|
||||
if "/" in fecha_orig:
|
||||
p = fecha_orig.split("/")
|
||||
if len(p) >= 2: mes_orig = str(int(p[1]))
|
||||
elif "-" in fecha_orig:
|
||||
p = fecha_orig.split("-")
|
||||
if len(p) >= 2: mes_orig = str(int(p[1]))
|
||||
analisis.append({
|
||||
"num_indice": idx,
|
||||
"fecha_supabase": fecha_orig,
|
||||
"mes_detectado": mes_orig,
|
||||
"mes_filtrado": str(int(mes)),
|
||||
"coincide_mes": mes_orig == str(int(mes)),
|
||||
"esta_en_sql_este_mes": idx in indices_sql,
|
||||
"deberia_salir_rojo": (mes_orig == str(int(mes))) and (idx not in indices_sql),
|
||||
})
|
||||
return {
|
||||
"supabase_conectado": conectado,
|
||||
"error_conexion": error_conexion,
|
||||
"total_fechas_en_supabase": len(fechas),
|
||||
"indices_en_sql_este_mes": indices_sql,
|
||||
"analisis": analisis,
|
||||
}
|
||||
return services.alertas()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/api/cobranza/detalle-todos")
|
||||
def get_cobranza_detalle_todos(ano: int, mes: int, sectorista: str = "TODOS"):
|
||||
@app.get("/api/debug/cursos")
|
||||
def debug_cursos(ano: str = "2026", mes: str = "1"):
|
||||
"""Diagnóstico: lista cursos del periodo con inscritos, tipo y meta."""
|
||||
try:
|
||||
return services.cobranza_detalle_todos(ano, mes, sectorista)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
# ── USUARIOS ───────────────────────────────────────────────────────────────
|
||||
@app.get("/api/alumno/estado")
|
||||
def get_alumno_estado(matricula: str):
|
||||
try:
|
||||
return services.alumno_estado_obtener(matricula)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.get("/api/alumnos/por-retirar")
|
||||
def get_alumnos_por_retirar():
|
||||
try:
|
||||
return services.alumnos_por_retirar()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.post("/api/alumno/estado/guardar")
|
||||
def post_alumno_estado(payload: dict = Body(...)):
|
||||
try:
|
||||
return services.alumno_estado_guardar(
|
||||
payload.get("matricula",""),
|
||||
payload.get("estado","EN CURSO"),
|
||||
payload.get("observaciones",[]),
|
||||
payload.get("usuario"))
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.get("/api/usuarios")
|
||||
def get_usuarios():
|
||||
try:
|
||||
return services.usuarios_listar()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.post("/api/usuarios/crear")
|
||||
def post_usuario_crear(payload: dict = Body(...)):
|
||||
try:
|
||||
return services.usuarios_crear(
|
||||
payload.get("email",""), payload.get("password",""),
|
||||
payload.get("nombre",""), payload.get("rol","COBRANZA"))
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.post("/api/usuarios/rol")
|
||||
def post_usuario_rol(payload: dict = Body(...)):
|
||||
try:
|
||||
return services.usuarios_actualizar_rol(payload.get("id",""), payload.get("rol",""))
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.post("/api/usuarios/eliminar")
|
||||
def post_usuario_eliminar(payload: dict = Body(...)):
|
||||
try:
|
||||
return services.usuarios_eliminar(payload.get("id",""))
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
# ── VENDEDORES MANUALES (Comisiones) ───────────────────────────────────────
|
||||
@app.get("/api/comisiones/vendedores")
|
||||
def get_vend_manuales(ano: int, mes: int):
|
||||
try:
|
||||
return services.vendedores_manuales_listar(ano, mes)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.post("/api/comisiones/vendedores/crear")
|
||||
def post_vend_manual_crear(payload: dict = Body(...)):
|
||||
try:
|
||||
return services.vendedores_manuales_crear(
|
||||
payload.get("nombre",""), payload.get("descripcion",""),
|
||||
payload.get("fch_emision",""), payload.get("monto",0),
|
||||
payload.get("ano"), payload.get("mes"))
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.post("/api/comisiones/vendedores/eliminar")
|
||||
def post_vend_manual_eliminar(payload: dict = Body(...)):
|
||||
try:
|
||||
return services.vendedores_manuales_eliminar(payload.get("id",""))
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/comisiones/override/guardar")
|
||||
def post_com_override_guardar(payload: dict = Body(...)):
|
||||
try:
|
||||
return services.comisiones_override_guardar(payload.get("registros", []))
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.post("/api/comisiones/override/restaurar")
|
||||
def post_com_override_restaurar(payload: dict = Body(...)):
|
||||
try:
|
||||
return services.comisiones_override_restaurar(payload.get("num_matricula",""))
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/comisiones/vendedores/actualizar")
|
||||
def post_vend_manual_actualizar(payload: dict = Body(...)):
|
||||
try:
|
||||
return services.vendedores_manuales_actualizar(
|
||||
payload.get("id",""), payload.get("descripcion",""),
|
||||
payload.get("fch_emision",""), payload.get("monto",0))
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/comisiones/vendedores/lote")
|
||||
def post_vend_manual_lote(payload: dict = Body(...)):
|
||||
try:
|
||||
return services.vendedores_manuales_guardar_lote(
|
||||
payload.get("nombre",""), payload.get("ano"), payload.get("mes"),
|
||||
payload.get("filas", []))
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/api/comisiones/detalle-todos")
|
||||
def get_comisiones_detalle_todos(ano: int, mes: int):
|
||||
try:
|
||||
return services.comisiones_detalle_todos(ano, mes)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/api/comisiones/config")
|
||||
def get_comisiones_config(ano: int = Query(...), mes: int = Query(...)):
|
||||
try:
|
||||
return services.comisiones_config_listar(ano, mes)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/comisiones/config/guardar")
|
||||
def post_comisiones_config(payload: dict = Body(...)):
|
||||
try:
|
||||
return services.comisiones_config_guardar(payload.get("ano"), payload.get("mes"), payload.get("filas", []))
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
# ── ASESORES (Chatwoot) ────────────────────────────────────────────────────
|
||||
CHATWOOT_URL = "https://gestor.escueladerefrigeracion.edu.pe"
|
||||
ACCESS_TOKEN = "4anazHvZnvKLtup8biu5Zuoh"
|
||||
ACCOUNT_ID = "1"
|
||||
CHATWOOT_HEADERS = {"api_access_token": ACCESS_TOKEN, "Content-Type": "application/json"}
|
||||
|
||||
|
||||
@app.get("/api/asesores")
|
||||
def get_asesores():
|
||||
import requests
|
||||
try:
|
||||
url = f"{CHATWOOT_URL}/api/v1/accounts/{ACCOUNT_ID}/agents"
|
||||
r = requests.get(url, headers=CHATWOOT_HEADERS, timeout=10)
|
||||
r.raise_for_status()
|
||||
return {"agentes": r.json()}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/asesores/estado")
|
||||
def set_asesor_estado(agent_id: int = Query(...), online: bool = Query(...)):
|
||||
import requests, time
|
||||
url = f"{CHATWOOT_URL}/api/v1/accounts/{ACCOUNT_ID}/agents/{agent_id}"
|
||||
estado = "online" if online else "offline"
|
||||
try:
|
||||
requests.patch(url, headers=CHATWOOT_HEADERS, json={"auto_offline": False}, timeout=10)
|
||||
time.sleep(0.5)
|
||||
r = requests.patch(url, headers=CHATWOOT_HEADERS,
|
||||
json={"availability_status": estado, "availability": estado}, timeout=10)
|
||||
r.raise_for_status()
|
||||
return {"status": "ok", "estado": estado}
|
||||
return services.debug_cursos(ano, mes)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=False)
|
||||
uvicorn.run("main:app", host="0.0.0.0", port=8001, reload=False)
|
||||
|
||||
Reference in New Issue
Block a user