Propósito
Generar un reporte semanal de pacientes con balance pendiente en Open Dental, separando lo que el seguro debe vs. lo que el paciente debe, para que Billing Jr pueda priorizar follow-up.
Pre-checks
| Check | Cómo verificar |
|---|---|
| Cliente tiene OD API habilitada | "has_od_api": true en clients/<id>.json |
| venv activado | Prompt empieza con (.venv) |
| Acceso a Google Sheet del cliente | Configurado en clients/<id>.json bajo google_sheet |
Cómo se ejecuta
1Modo básico (todos los pacientes con balance)
python patient_ar_audit.py <client-id>
Por default escanea procedures de los últimos 365 días, agrupa por paciente, calcula balance, y escribe al Google Sheet del cliente.
2Con filtro de días mínimos pendientes
python patient_ar_audit.py <client-id> --min-days 30
Skipea claims más nuevos que X días (default 30). Útil para no llamar a un seguro por un claim de hace 5 días.
3Modo "all balances" (sin Stedi check)
python patient_ar_audit.py <client-id> --all-with-balance --no-stedi-check
Para clientes con migración desde otro PMS (Hallandale tipo). Lista TODOS los pacientes con balance sin consultar status en Stedi.
Qué datos genera el reporte
| Columna | Significado |
|---|---|
| PatNum | Patient Number en OD |
| Chart # | Chart Number (si tiene) |
| Patient Name | Apellido, Nombre |
| Last Procedure DOS | Fecha del último procedure completo |
| Days Outstanding | Días desde el último procedure |
| BalTotal | Total que el paciente debe |
| InsBalance | De ese total, cuánto debería el seguro |
| PatBalance | De ese total, cuánto debe el paciente |
| Insurance Carrier | Carrier primario del paciente |
| Subscriber ID | ID del subscriber |
| Group # | Group number |
| Phone | Teléfono del paciente para contacto |
| Action | Acción sugerida por el sistema |
Workflow para trabajar el reporte
1Abrir el Google Sheet del cliente
Tab con prefijo del cliente + "Patient AR Audit" + timestamp.
2Ordenar por Days Outstanding (descendente)
Atacar los más viejos primero (filing limit risk).
3Para cada paciente con InsBalance > 0:
- ¿El claim fue enviado? → si no, enviarlo
- ¿El claim fue rechazado? → ver SOP-CL-005 (Manejo de Rechazos)
- ¿Más de 60 días? → status check Stedi 276 + escalación si necesario
- ¿Más de 90 días? → considerar apelación
4Para cada paciente con PatBalance > 0:
- Llamar al paciente (script en el dashboard)
- Ofrecer payment plan si el balance es +$500
- Anotar el contacto en OD
5Marcar resolved en el sheet
Cada fila trabajada → marcar status. Próximo run no la vuelve a mostrar si el balance bajó a 0.
Casos especiales que el sistema detecta automáticamente
| Detección | Por qué importa |
|---|---|
| Insurance payment recibido pero claim sigue marcado pendiente | Hay que postear el EOB en OD |
| InsPayAmt > 0 pero claim no linked | Datos orphan — investigar y fixear |
| BalTotal <= 0 | El balance ya está cero — skipear |
| WriteOff/DedApplied > 0 sin payment | Adjustment ya aplicado — verificar si es esperado |
| Procedures no-ADA (DEDUC, ADJUST) | Filtrar — no son procedures reales |
Troubleshooting
| Error | Solución |
|---|---|
ReadTimeout intermitente | Re-correr — tiene retry automático con backoff exponencial |
| OD devuelve 0 pacientes | Endpoint /patients no expone BalTotal. El script ya pivotó a usar /procedurelogs |
Filter DateTStamp rechazado | OD no acepta ese filtro. El script ya pagina todo y filtra client-side |
| Hits a row limit del Sheet | El script ya llama _ensure_capacity() antes de cada write |
Comandos de referencia
# Modo básico (recomendado para clientes con OD API) python patient_ar_audit.py benitez-dental # Con threshold mínimo python patient_ar_audit.py casas-family-dentistry --min-days 30 # Modo legacy (clientes migrados) python patient_ar_audit.py hallandale-dental --all-with-balance --no-stedi-check # Solo un paciente específico (debug) python patient_ar_audit.py benitez-dental --single-patient 12345
