fix: group ventas by NUM_FACTURA, not IDRECEPCION

A single CMXC invoice (e.g. CMXC-307) can have multiple receptions.
Previously each reception was sent as a separate ventas, causing TNS
to accept the first and reject the rest with "ya existe" — leaving
only 1/N of the services registered.

Now agrupar_por_factura groups all receptions sharing the same
NUM_FACTURA into one ventas JSON with all their services in
detallePedido. One factura = one send.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-07-27 17:36:21 -05:00
co-authored by Claude Sonnet 4.6
parent 531fb29122
commit 7f2dffc63e
2 changed files with 17 additions and 6 deletions
+8 -6
View File
@@ -11,6 +11,7 @@ from app.services.json_generator import (
generar_tercero_api,
generar_rda_paciente,
agrupar_por_recepcion,
agrupar_por_factura,
generar_factura_venta,
)
from app.services.api_client import get_tns_token, TNS_BASE
@@ -453,13 +454,13 @@ async def preview_automation(
if rda_por_pac.get(str(p.get("CODIGO", "")))
]
grupos_vta_all = agrupar_por_recepcion(rows_vta)
grupos_vta_all = agrupar_por_factura(rows_vta)
grupos_vta = {k: v for k, v in grupos_vta_all.items()
if str(v[0].get("CODCONTRATO") or "").strip() not in excluded_ventas
and int(v[0].get("NUM_FACTURA") or 0) != 0}
ventas_preview = []
for id_rec, grupo_rows in grupos_vta.items():
for num_fac_key, grupo_rows in grupos_vta.items():
num_fac = str(grupo_rows[0].get("NUM_FACTURA") or "").strip()
prefijo = str(grupo_rows[0].get("PREFIJO") or "").strip()
venta_json = generar_factura_venta(grupo_rows, default_vendedor="00",
@@ -732,7 +733,7 @@ async def run_automation(
# ── PASO 4: Enviar Facturas Venta ─────────────────────────────────────────
excluded_ventas = load_excluded_ventas_set()
grupos_vta_all = agrupar_por_recepcion(rows_vta)
grupos_vta_all = agrupar_por_factura(rows_vta)
grupos_vta = {k: v for k, v in grupos_vta_all.items()
if str(v[0].get("CODCONTRATO") or "").strip() not in excluded_ventas
and int(v[0].get("NUM_FACTURA") or 0) != 0}
@@ -741,13 +742,14 @@ async def run_automation(
endpoint_venta = f"{TNS_BASE}/v2/facturacion/Ventas/Crear?codigosucursal={api_sucursal or '00'}"
if "ventas" in pasos_set:
async with httpx.AsyncClient(timeout=timeout) as client:
for id_rec, grupo_rows in grupos_vta.items():
for num_fac_key, grupo_rows in grupos_vta.items():
num_fac = str(grupo_rows[0].get("NUM_FACTURA") or "").strip()
prefijo = str(grupo_rows[0].get("PREFIJO") or "").strip()
venta_json = generar_factura_venta(grupo_rows, default_vendedor="00",
default_prefijo=prefijo_def, numero_override=num_fac)
factura_display = f"{prefijo}-{num_fac}"
contrato_vta = str(grupo_rows[0].get("CODCONTRATO") or "").strip()
id_rec_display = str(grupo_rows[0].get("IDRECEPCION") or "")
try:
resp = await client.post(endpoint_venta, json=venta_json, headers=headers)
status, msg = _parse_tns_resp(resp)
@@ -760,7 +762,7 @@ async def run_automation(
resultado["paso4_ventas"]["enviados"] += 1
resultado["paso4_ventas"]["detalle"].append({
"idrecepcion": id_rec,
"idrecepcion": id_rec_display,
"factura": factura_display,
"paciente": str(grupo_rows[0].get("COD_PACIENTE", "")),
"examenes": len(grupo_rows),
@@ -769,7 +771,7 @@ async def run_automation(
_guardar_envio(user["user_id"], "ventas", factura_display, venta_json, msg, status,
fecha_inicio=fecha, fecha_fin=fecha, servicios=len(grupo_rows),
idrecepcion=id_rec, contrato=contrato_vta,
idrecepcion=id_rec_display, contrato=contrato_vta,
cedula=pac_map.get(str(grupo_rows[0].get("COD_PACIENTE", "")), ""))
r1 = resultado["paso1_terceros"]
+9
View File
@@ -125,6 +125,15 @@ def agrupar_por_recepcion(rows: list) -> dict:
return grupos
def agrupar_por_factura(rows: list) -> dict:
"""Agrupa filas por NUM_FACTURA — para ventas CMXC donde una factura puede tener varias recepciones."""
grupos = defaultdict(list)
for row in rows:
key = str(row.get("NUM_FACTURA") or "")
grupos[key].append(dict(row))
return grupos
def generar_rda_paciente(rows: list, default_profesional: str = "", default_especialidad: str = "",
default_remisionante: str = "00", default_prefijo: str = "00",
numero_override: str = "", contrato_map: dict = None,