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:
- Corre
python utils/match_report.pypara ver quién tienezk_user_idconfigurado y quién no. - Completa
zk_user_iden la ficha del empleado en Odoo. - Revisa que su ID no esté en
[sync] exclude_zk_idsni en elexclude_zk_idsde 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:
- Si el empleado ya marcó salida, espera la siguiente corrida: los registros
nuevos pisan a los pendientes y el
check_outse corrige. - Si marcó salida antes de
exit_hour, ese punch no cuenta como salida. Ajusta[sync] exit_houro corrige conpython utils/fix_attendance.py --fix-salidas. - 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:
-
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.1El script escanea la subred
/24buscando el puerto4370abierto, verifica cuáles responden como dispositivo ZK con la contraseña deconfig.ini, y ofrece actualizar la IP (confirmacións/N). -
Reinicia el servicio para que tome la IP nueva:
-
Confirma que vuelve a leer el reloj:
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:
- Verifica físicamente que el reloj esté encendido y con cable/WiFi.
- Prueba otra subred:
utils\find_zk.py --subred <prefijo>. - Si aparecen hosts con el puerto abierto pero ninguno responde como ZK, es
señal de que la contraseña del dispositivo en
config.iniya 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.