diff --git a/app/routes/ventas.py b/app/routes/ventas.py index 9905444..ed3d097 100644 --- a/app/routes/ventas.py +++ b/app/routes/ventas.py @@ -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 diff --git a/app/templates/ventas.html b/app/templates/ventas.html index 645e70c..cff4287 100644 --- a/app/templates/ventas.html +++ b/app/templates/ventas.html @@ -193,37 +193,41 @@ function renderTabla() { } function filaHtml(item) { - const id = item.idrecepcion; + const id = item.factura_key; + const sid = escAttr(id); const env = item.enviado; const examsStr = Array.isArray(item.examenes) ? item.examenes.join(', ') : (item.examenes || ''); + const rowId = 'row-' + id.replace(/[^a-zA-Z0-9]/g, '_'); + const estadoId = 'estado-' + id.replace(/[^a-zA-Z0-9]/g, '_'); + const btnId = 'btnenv-' + id.replace(/[^a-zA-Z0-9]/g, '_'); let badgeHtml, rowCls, accionHtml; if (!env) { badgeHtml = `Pendiente`; rowCls = ''; - accionHtml = ``; + accionHtml = ``; } else if (env.status === 'success') { const at = (env.at || '').slice(0, 16).replace('T', ' '); badgeHtml = `✓ Enviado`; rowCls = 'bg-green-50/50 opacity-80'; - accionHtml = ``; + accionHtml = ``; } else { const tip = escAttr(env.mensaje || 'Error'); badgeHtml = `✗ Error`; rowCls = 'bg-red-50/50'; - accionHtml = ``; + accionHtml = ``; } - return `