Saltar a contenido

Troubleshooting — Control de Asistencia

Primer paso siempre: python utils/status.py (conectividad, última sync, pendientes y últimos errores) y logs/sync.log.

Un empleado no aparece en Odoo

Síntoma: sus marcaciones existen en el reloj pero no hay hr.attendance, y logs/unmatched.log reporta el error.

Causa probable: el empleado no tiene zk_user_id en hr.employee, o su ID está en exclude_zk_ids.

Solución:

  1. Corre python utils/match_report.py para ver quién tiene zk_user_id configurado y quién no.
  2. Completa zk_user_id en la ficha del empleado en Odoo.
  3. Revisa que su ID no esté en [sync] exclude_zk_ids ni en el exclude_zk_ids de esa instancia.

La asistencia queda abierta (sin check_out)

Síntoma: el registro del día muestra entrada pero no salida.

Causa probable: aún no hay ningún punch con hora >= exit_hour, así que last_punch == first_punch. Es el comportamiento esperado durante la jornada.

Solución:

  1. Si el empleado ya marcó salida, espera la siguiente corrida: los registros nuevos pisan a los pendientes y el check_out se corrige.
  2. Si marcó salida antes de exit_hour, ese punch no cuenta como salida. Ajusta [sync] exit_hour o corrige con python utils/fix_attendance.py --fix-salidas.
  3. Para asistencias abiertas de días anteriores, el servicio las cierra solo en cada corrida; manualmente: python utils/close_open_attendances.py.

Aparecen asistencias a medianoche que nadie marcó

Síntoma: registros con check_in a las 00:00 locales (05:00 UTC en Panamá).

Causa probable: Odoo HR las crea automáticamente para empleados activos con horario asignado, aunque no hayan marcado.

Solución: delete_midnight_phantoms() las elimina en cada corrida (ventana de ±10 min). Si quedan restos, usa python utils/delete_attendance.py --fecha <YYYY-MM-DD> --empleado "<nombre>" (prueba primero con --dry-run).

Jornada de pocos minutos (entrada = salida casi inmediata)

Síntoma: asistencia cerrada con duración de 0–5 minutos.

Causa probable: doble-tap en el sensor a la entrada, interpretado por un ciclo anterior como entrada + salida.

Solución: open_phantom_attendances() le borra el check_out en la siguiente corrida para que Odoo acepte la salida real. No requiere intervención.

El reloj cambió de IP tras reiniciarse

Es el fallo más frecuente del servicio.

Síntoma: el log muestra error de conexión al dispositivo en cada corrida y dejan de entrar marcaciones. utils/status.py reporta el reloj como inalcanzable. La corrida continúa con los demás relojes (si hay).

Causa: el F22 se reinició —corte de luz, reinicio manual, reinicio del router— y al volver pidió una IP nueva por DHCP. La dirección que quedó en config.ini ya no apunta al reloj. Como la configuración se lee solo al arrancar, el servicio sigue insistiendo contra la IP vieja indefinidamente.

Solución:

  1. Localiza el reloj en la red:

    .\.venv\Scripts\python.exe utils\find_zk.py
    
    # Si el reloj está en otra subred:
    .\.venv\Scripts\python.exe utils\find_zk.py --subred 192.168.1
    

    El script escanea la subred /24 buscando el puerto 4370 abierto, verifica cuáles responden como dispositivo ZK con la contraseña de config.ini, y ofrece actualizar la IP (confirmación s/N).

  2. Reinicia el servicio para que tome la IP nueva:

    Restart-Service ZKSyncService
    
  3. Confirma que vuelve a leer el reloj:

    .\.venv\Scripts\python.exe utils\marcaciones_hoy.py
    

    Lee directo del dispositivo, sin pasar por Odoo. No se pierden marcaciones: el reloj las conserva y cada corrida descarga todo el día desde medianoche.

El script no reinicia el servicio

find_zk.py solo actualiza config.ini y luego imprime el comando Restart-Service ZKSyncService para que lo ejecutes. Si te saltas el paso 2, el servicio sigue usando la IP anterior.

Pendiente: reservar una IP fija para el reloj

Buscar la IP a mano es un parche, no la solución. Lo correcto es asignar al F22 una IP estática mediante reserva DHCP en el router: se ata la MAC del reloj a una dirección fija, de modo que conserve siempre la misma IP aunque se reinicie, y el servicio nunca pierda la conexión.

Esta configuración está pendiente. Mientras no se haga, el fallo va a repetirse en cada reinicio del reloj y habrá que aplicar el procedimiento de arriba. Coordinar con quien administre la red de la oficina para:

  • Registrar la MAC del F22 y reservarle una IP fuera del rango dinámico.
  • Documentar aquí la IP asignada una vez configurada.

El reloj no responde (otras causas)

Si find_zk.py no encuentra nada en la subred:

Causas probables: el reloj está apagado o desconectado de la red, quedó en otra subred/VLAN, el firewall bloquea el puerto 4370, o cambió la contraseña del dispositivo.

Solución:

  1. Verifica físicamente que el reloj esté encendido y con cable/WiFi.
  2. Prueba otra subred: utils\find_zk.py --subred <prefijo>.
  3. Si aparecen hosts con el puerto abierto pero ninguno responde como ZK, es señal de que la contraseña del dispositivo en config.ini ya no coincide.

Los pendientes no se envían

Síntoma: data/pending_<odoo>.json no se vacía.

Causa probable: Odoo sigue devolviendo errors para esos registros (lo más común: zk_user_id faltante).

Solución: revisa logs/unmatched.log, corrige la causa en Odoo y espera la siguiente corrida. Los pendientes de días anteriores se descartan automáticamente, así que no se acumulan.

Cambié config.ini y no pasó nada

Causa: la configuración se lee solo al arrancar.

Solución: Restart-Service ZKSyncService.

No puedo detener o desinstalar el servicio

Causa probable: install_service.ps1 crea el servicio como ZKSyncService, pero uninstall_service.ps1 y el README.md usan ZKTecoSync.

Solución: confirma el nombre real con Get-Service *ZK* y opéralo con ese nombre. Ver Instalar y operar el servicio de Windows.