fix: refactor ventas page to group by factura instead of idrecepcion

- _agrupar now keys by PREFIJO-NUM_FACTURA so all services of a factura are grouped together
- _is_sent checks by factura key instead of idrecepcion
- send-one and send endpoints accept/return factura_key instead of idrecepcion
- ventas.html JS updated: factura_key used throughout (filaHtml, marcarFila, enviarUno, enviarTodosPendientes, verDetalle)
- DOM element IDs sanitized with _safeId() to handle dashes in keys like CMXC-309
- onclick handlers now pass string keys with proper quoting

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-07-28 17:08:20 -05:00
co-authored by Claude Sonnet 4.6
parent c1925ef6da
commit 8ed8f0999f
3 changed files with 54 additions and 44 deletions
+21 -21
View File
@@ -50,10 +50,14 @@ def _query_rows(cfg, query_text, factura, fecha_inicio, fecha_fin):
def _agrupar(rows):
"""Agrupa por PREFIJO+NUM_FACTURA para incluir todos los servicios de una factura."""
from collections import defaultdict
grupos = defaultdict(list)
for row in rows:
grupos[row.get("IDRECEPCION")].append(dict(row))
prefijo = str(row.get("PREFIJO") or "").strip()
num = str(row.get("NUM_FACTURA") or "").strip()
key = f"{prefijo}-{num}"
grupos[key].append(dict(row))
return dict(grupos)
@@ -64,11 +68,11 @@ def _build_venta(grupo_rows, cfg):
default_prefijo=prefijo_def, numero_override=num_fac)
def _is_sent(idrecepcion):
def _is_sent(factura_key):
conn = get_connection()
row = conn.execute(
"SELECT status, mensaje_tns, created_at FROM envios WHERE idrecepcion=? AND tipo='ventas' ORDER BY id DESC LIMIT 1",
(idrecepcion,)
"SELECT status, mensaje_tns, created_at FROM envios WHERE factura=? AND tipo='ventas' ORDER BY id DESC LIMIT 1",
(factura_key,)
).fetchone()
conn.close()
if row:
@@ -148,14 +152,12 @@ async def preview_ventas(
if str(v[0].get("CODCONTRATO") or "").strip() not in excluded_ventas}
items = []
for key, grupo_rows in grupos.items():
enviado = _is_sent(key)
for factura_key, grupo_rows in grupos.items():
enviado = _is_sent(factura_key)
venta_json = _build_venta(grupo_rows, cfg)
num_fac = str(grupo_rows[0].get("NUM_FACTURA") or "")
prefijo = str(grupo_rows[0].get("PREFIJO") or "").strip()
items.append({
"idrecepcion": key,
"factura": f"{prefijo}-{num_fac}",
"factura_key": factura_key,
"factura": factura_key,
"paciente": grupo_rows[0].get("COD_PACIENTE", ""),
"contrato": str(grupo_rows[0].get("CODCONTRATO") or "").strip(),
"examenes": [r.get("COD_EXAMEN", "") for r in grupo_rows],
@@ -181,7 +183,7 @@ async def preview_ventas(
@router.post("/send-one")
async def send_one(
request: Request, user: dict = Depends(get_current_user),
idrecepcion: int = Form(...), query_id: int = Form(...),
factura_key: str = Form(...), query_id: int = Form(...),
factura: str = Form(""), fecha_inicio: str = Form(...), fecha_fin: str = Form(...),
):
conn = get_connection()
@@ -196,9 +198,9 @@ async def send_one(
return JSONResponse({"success": False, "message": err})
grupos = _agrupar(rows)
grupo_rows = grupos.get(idrecepcion)
grupo_rows = grupos.get(factura_key)
if not grupo_rows:
return JSONResponse({"success": False, "message": f"Registro {idrecepcion} no encontrado"})
return JSONResponse({"success": False, "message": f"Factura {factura_key} no encontrada"})
token, token_err = await get_tns_token(
cfg.get("tns_empresa", ""), cfg.get("tns_usuario", ""), cfg.get("tns_password", "")
@@ -225,13 +227,13 @@ async def send_one(
except Exception as ex:
msg_tns = str(ex)
_guardar_envio(user["user_id"], factura_display, idrecepcion, contrato,
_guardar_envio(user["user_id"], factura_display, None, contrato,
venta_json, raw_resp, ok_v, fecha_inicio, fecha_fin, len(grupo_rows))
log_activity(user["user_id"], user["username"], "venta_enviada",
f"Factura {factura_display} | {'OK' if ok_v else 'ERROR: '+msg_tns[:80]}",
get_ip(request))
return JSONResponse({"success": ok_v, "message": msg_tns, "raw_tns": raw_resp, "idrecepcion": idrecepcion})
return JSONResponse({"success": ok_v, "message": msg_tns, "raw_tns": raw_resp, "factura_key": factura_key})
@router.post("/send")
@@ -261,6 +263,7 @@ async def send_ventas(
if solo_pendientes == "1":
grupos = {k: v for k, v in grupos.items() if not _is_sent(k)}
token, token_err = await get_tns_token(
cfg.get("tns_empresa", ""), cfg.get("tns_usuario", ""), cfg.get("tns_password", "")
)
@@ -272,11 +275,8 @@ async def send_ventas(
resultados = []
async with httpx.AsyncClient(timeout=int(cfg.get("api_timeout", 30))) as client:
for key, grupo_rows in grupos.items():
for factura_key, grupo_rows in grupos.items():
venta_json = _build_venta(grupo_rows, cfg)
num_fac = str(grupo_rows[0].get("NUM_FACTURA") or "").strip()
prefijo = str(grupo_rows[0].get("PREFIJO") or "").strip()
factura_display = f"{prefijo}-{num_fac}"
contrato = str(grupo_rows[0].get("CODCONTRATO") or "").strip()
raw_resp = ""
ok_v = False
@@ -288,9 +288,9 @@ async def send_ventas(
except Exception as ex:
msg_tns = str(ex)
_guardar_envio(user["user_id"], factura_display, key, contrato,
_guardar_envio(user["user_id"], factura_key, None, contrato,
venta_json, raw_resp, ok_v, fecha_inicio, fecha_fin, len(grupo_rows))
resultados.append({"idrecepcion": key, "success": ok_v, "msg": msg_tns})
resultados.append({"factura": factura_key, "success": ok_v, "msg": msg_tns})
ok_count = sum(1 for r in resultados if r["success"])
err_count = len(resultados) - ok_count