feat: enrich sync log with exams, diagnosis, EPS and valor per patient
- scheduler.py: after sync_todos(), inject diagnostico, nit_empresa, valor_total and examenes_det (cups/nombre/precio) into each detalle entry - whatsapp_sync.py: guardar_sync_log stores full enriched fields in detalle_json - envios_erp.html: expanded row shows per-patient card with Dx, EPS, valor and a CUPS/examen/precio table — scrollable, max-h-96 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
e044f21e3e
commit
cd63683869
@@ -167,6 +167,21 @@ async def sync_recientes(ventana_min: int = 2) -> dict:
|
||||
resultado = await sync_todos(recientes, ingest_url, wa_key, timeout,
|
||||
modo="upsert", examenes_map=examenes_map)
|
||||
|
||||
# Enriquecer detalle con exámenes, diagnóstico, empresa y valor para el log
|
||||
for entry in resultado.get("detalle", []):
|
||||
doc = entry.get("doc", "")
|
||||
exams = examenes_map.get(doc, [])
|
||||
if exams:
|
||||
first = exams[0]
|
||||
entry["diagnostico_cod"] = first.get("diagnostico_cod", "")
|
||||
entry["diagnostico_nombre"] = first.get("diagnostico_nombre", "")
|
||||
entry["nit_empresa"] = first.get("nit_empresa", "")
|
||||
entry["valor_total"] = first.get("valor_total")
|
||||
entry["examenes_det"] = [
|
||||
{"cups": e.get("cups", ""), "nombre": e.get("nombre", ""), "precio": e.get("precio")}
|
||||
for e in exams
|
||||
]
|
||||
|
||||
if resultado["total"] > 0:
|
||||
guardar_sync_log(resultado, None, origen="scheduler", modo="upsert")
|
||||
|
||||
|
||||
@@ -31,9 +31,17 @@ def guardar_sync_log(
|
||||
detalle_json = None
|
||||
if detalle_raw:
|
||||
detalle_json = json.dumps(
|
||||
[{"doc": d["doc"], "nombre": d["nombre"], "action": d.get("action", ""),
|
||||
"examenes": d.get("examenes_guardados", 0)}
|
||||
for d in detalle_raw],
|
||||
[{
|
||||
"doc": d["doc"],
|
||||
"nombre": d["nombre"],
|
||||
"action": d.get("action", ""),
|
||||
"examenes_cnt": d.get("examenes_guardados", 0),
|
||||
"diagnostico_cod": d.get("diagnostico_cod", ""),
|
||||
"diagnostico_nombre": d.get("diagnostico_nombre", ""),
|
||||
"nit_empresa": d.get("nit_empresa", ""),
|
||||
"valor_total": d.get("valor_total"),
|
||||
"examenes_det": d.get("examenes_det", []),
|
||||
} for d in detalle_raw],
|
||||
ensure_ascii=False,
|
||||
)
|
||||
|
||||
|
||||
@@ -254,6 +254,55 @@ async function cargarHistorialScheduler() {
|
||||
const errDet = r.errores_det ? JSON.parse(r.errores_det) : [];
|
||||
const pacientes = r.detalle_json ? JSON.parse(r.detalle_json) : [];
|
||||
const rowId = 'hist-det-' + i;
|
||||
|
||||
const detHtml = pacientes.map((p, pi) => {
|
||||
const badge = p.action === 'created' ? '<span class="bg-blue-100 text-blue-700 px-1.5 py-0.5 rounded">Nuevo</span>'
|
||||
: p.action === 'updated' ? '<span class="bg-green-100 text-green-700 px-1.5 py-0.5 rounded">Actualizado</span>'
|
||||
: p.action === 'skipped' ? '<span class="bg-gray-100 text-gray-400 px-1.5 py-0.5 rounded">Omitido</span>'
|
||||
: `<span class="bg-red-100 text-red-600 px-1.5 py-0.5 rounded">${p.action}</span>`;
|
||||
|
||||
const diag = p.diagnostico_cod
|
||||
? `<span class="text-orange-600">${p.diagnostico_cod}</span>${p.diagnostico_nombre ? ' · ' + p.diagnostico_nombre : ''}`
|
||||
: '<span class="text-gray-300">Sin diagnóstico</span>';
|
||||
|
||||
const empresa = p.nit_empresa
|
||||
? `<span class="text-teal-600">${p.nit_empresa}</span>`
|
||||
: '<span class="text-gray-300">Sin empresa</span>';
|
||||
|
||||
const valor = p.valor_total > 0
|
||||
? `<span class="text-yellow-700">$${Number(p.valor_total).toLocaleString('es-CO')}</span>`
|
||||
: '';
|
||||
|
||||
const exams = (p.examenes_det || []).map(e =>
|
||||
`<tr class="border-b border-gray-50">
|
||||
<td class="py-0.5 pr-3 font-mono text-purple-600">${e.cups || '—'}</td>
|
||||
<td class="py-0.5 pr-3 text-gray-600">${e.nombre || '—'}</td>
|
||||
<td class="py-0.5 text-right text-gray-500">${e.precio != null ? '$' + Number(e.precio).toLocaleString('es-CO') : '—'}</td>
|
||||
</tr>`
|
||||
).join('');
|
||||
|
||||
return `<div class="mb-3 border border-gray-100 rounded-lg overflow-hidden">
|
||||
<div class="flex items-center gap-3 px-3 py-2 bg-gray-50 border-b border-gray-100">
|
||||
<span class="text-gray-400 font-mono text-xs">${p.doc}</span>
|
||||
<span class="font-medium text-gray-700 flex-1">${p.nombre}</span>
|
||||
${badge}
|
||||
</div>
|
||||
<div class="px-3 py-1.5 flex flex-wrap gap-x-4 gap-y-0.5 text-xs border-b border-gray-50">
|
||||
<span>Dx: ${diag}</span>
|
||||
<span>EPS: ${empresa}</span>
|
||||
${valor ? `<span>Total: ${valor}</span>` : ''}
|
||||
</div>
|
||||
${exams ? `<table class="w-full text-xs px-3">
|
||||
<thead><tr class="text-gray-400 border-b border-gray-100">
|
||||
<th class="py-1 pr-3 text-left font-medium px-3">CUPS</th>
|
||||
<th class="py-1 pr-3 text-left font-medium">Examen</th>
|
||||
<th class="py-1 text-right font-medium pr-3">Precio</th>
|
||||
</tr></thead>
|
||||
<tbody class="px-3">${exams}</tbody>
|
||||
</table>` : '<p class="text-xs text-gray-300 px-3 py-1">Sin exámenes registrados</p>'}
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
return `
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="py-2 pr-4 text-gray-500 whitespace-nowrap font-mono text-xs">${fecha}</td>
|
||||
@@ -273,22 +322,10 @@ async function cargarHistorialScheduler() {
|
||||
</td>
|
||||
</tr>
|
||||
${pacientes.length ? `
|
||||
<tr id="${rowId}" class="hidden bg-gray-50">
|
||||
<td colspan="7" class="px-4 pb-3 pt-1">
|
||||
<div class="text-xs text-gray-500 space-y-0.5 max-h-48 overflow-y-auto">
|
||||
${pacientes.map(p => {
|
||||
const badge = p.action === 'created' ? '<span class="text-blue-600">Nuevo</span>'
|
||||
: p.action === 'updated' ? '<span class="text-green-600">Actualizado</span>'
|
||||
: p.action === 'skipped' ? '<span class="text-gray-400">Omitido</span>'
|
||||
: `<span class="text-red-500">${p.action}</span>`;
|
||||
const exBadge = p.examenes > 0
|
||||
? `<span class="text-purple-500">${p.examenes} exám.</span>` : '';
|
||||
return `<div class="flex items-center gap-3 py-0.5 border-b border-gray-100">
|
||||
<span class="text-gray-400 w-28 shrink-0">${p.doc}</span>
|
||||
<span class="flex-1 truncate">${p.nombre}</span>
|
||||
${badge} ${exBadge}
|
||||
</div>`;
|
||||
}).join('')}
|
||||
<tr id="${rowId}" class="hidden">
|
||||
<td colspan="7" class="px-3 pb-4 pt-2">
|
||||
<div class="max-h-96 overflow-y-auto space-y-2 text-xs">
|
||||
${detHtml}
|
||||
</div>
|
||||
</td>
|
||||
</tr>` : ''}`;
|
||||
|
||||
Reference in New Issue
Block a user