# Taxes - Agente Huella ZK9500

Agente local que comunica el lector USB de huella **ZKTeco ZK9500** con el frontend de Taxes Software.

Corre en `http://127.0.0.1:7777` y solo escucha en loopback (no expone nada hacia la red). El frontend de Taxes le pega para enrolar empleados y, en la terminal de fichaje, para identificar al empleado por huella.

---

## Requisitos

1. **Windows 10 / 11** (64 bits).
2. **Lector ZK9500** conectado por USB.
3. **Python 3.9+** instalado y agregado al PATH.
4. **ZKFinger SDK 5.3** (driver oficial de ZKTeco). El paso 2 de la instalacion explica como conseguirlo.

> Nota: el numero **5.3** es del SDK nativo de ZKTeco (libzkfp.dll). El wrapper Python que usamos
> se llama `pyzkfp` y su version actual en pypi es **0.1.5** — son numeraciones distintas.

---

## 1. Instalacion del driver ZK + ZKFinger SDK 5.3

El lector ZK9500 **no funciona "plug and play"**. Necesita el driver USB y la libreria `libzkfp.dll` registrada en el sistema. Ambos vienen en el mismo instalador.

### Pasos

1. Descargar **ZKFinger SDK 5.3 for Windows** desde la pagina oficial:
   - https://www.zkteco.com.ar/Search/searchresult?searchKey=ZKFinger+SDK
   - Si pide registro, alternativa directa (espejo no oficial): buscar en Google `"ZKFinger_SDK_5.3" "Windows" download`.
   - El archivo se llama parecido a `ZKFinger SDK Free 5.3.0.42.exe` (~30 MB).
2. **Desconectar el ZK9500** antes de instalar.
3. Ejecutar el `.exe` como administrador, dejar las opciones por defecto. Esto:
   - Instala el driver USB.
   - Registra `libzkfp.dll` en `C:\Windows\System32` (lo que necesita `pyzkfp` para funcionar).
   - Instala una utilidad llamada **ZKFinger Demo** que sirve para verificar.
4. Reiniciar Windows.
5. Conectar el ZK9500. Windows debe reconocerlo como **"ZKTeco Live20R"** o **"ZK9500"** en el Administrador de dispositivos.
6. Probar con la utilidad `ZKFinger Demo` que se instalo:
   - Boton **"Open"** -> tiene que decir que detecto 1 dispositivo.
   - Apoyar el dedo -> tiene que ver la imagen de la huella.

Si esto funciona, el SDK esta OK y se puede pasar al siguiente paso.

---

## 2. Instalacion del agente Python

### Modo desarrollo (con consola, viendo logs)

Doble clic a `run.bat`. La primera vez crea un entorno virtual e instala dependencias. Luego arranca Flask.

Salida esperada en la consola:

```
[2026-05-06 10:00:00] INFO taxes-agente-huella :: Taxes - Agente Huella ZK9500 v1.0.0
[2026-05-06 10:00:00] INFO taxes-agente-huella :: Escuchando en http://127.0.0.1:7777  (loopback)
[2026-05-06 10:00:00] INFO zk_reader :: Lector ZK abierto (1 dispositivo/s detectado/s)
```

### Modo manual

```bat
python -m venv venv
venv\Scripts\activate
pip install -r requirements.txt
python main.py
```

### Empaquetar como `.exe` para instalar en clientes

```bat
venv\Scripts\activate
pip install pyinstaller
build.bat
```

Genera `dist\TaxesAgenteHuella.exe`. Se puede agregar al inicio de Windows con un acceso directo en `shell:startup`.

### Generar el instalador todo-en-uno (recomendado para clientes)

Crea un unico `.exe` que instala el SDK ZK + el agente + lo registra en el inicio
de Windows. Para distribuir a kioscos / PCs de RRHH.

Requisitos previos:
- Python 3.9+ en el PATH.
- [Inno Setup 6](https://jrsoftware.org/isinfo.php) instalado.
- `ZK9500setup.exe` presente en esta carpeta (driver oficial de ZKTeco).

```bat
build_installer.bat
```

Genera `dist_installer\TaxesAgenteHuella-Setup.exe` (~40 MB). Lo que hace el
instalador en la PC del cliente:

1. Pide privilegios de administrador.
2. Mata cualquier instancia previa del agente.
3. Copia `TaxesAgenteHuella.exe` a `C:\Program Files\TaxesAgenteHuella\`.
4. Ejecuta `ZK9500setup.exe` (instalador oficial de ZKTeco) en modo interactivo
   asi el usuario ve los pasos del SDK.
5. Crea acceso directo en el inicio de Windows (opcional, marcado por defecto)
   y opcionalmente en escritorio.
6. Pregunta si lanzar el agente al final.

---

## 3. Verificar que el agente responde

Abrir `http://127.0.0.1:7777/status` en el browser. Tiene que devolver algo como:

```json
{
  "ok": true,
  "connected": true,
  "version": "1.0.0",
  "modelo": "ZK9500"
}
```

Si dice `"connected": false`, el SDK no encuentra el lector. Verificar:
- ZK9500 fisicamente conectado.
- ZKFinger SDK 5.3 instalado (paso 1).
- `ZKFinger Demo` lo ve OK.

---

## 4. Endpoints

| Metodo | Path        | Descripcion                                                 |
|--------|-------------|-------------------------------------------------------------|
| GET    | `/status`   | Estado del lector.                                          |
| POST   | `/enroll`   | Captura 3 muestras del mismo dedo y devuelve template merged. |
| POST   | `/capture`  | Captura simple (template + imagen).                         |
| POST   | `/identify` | Identify 1:N contra una lista de templates registrados.     |

### Ejemplo `/enroll`

Request:
```json
POST /enroll
{ "finger": 1 }
```

Response (despues de 3 capturas):
```json
{
  "ok": true,
  "finger": 1,
  "template": "AbcDef...==",   // base64, ~2 KB
  "image": "iVBORw0K...",      // base64 de la imagen BMP, ~30 KB
  "captures_done": 3
}
```

El frontend de Taxes manda este `template` al backend Laravel via `POST /sueldo/empleados/biometrico/{id}/huella`.

### Ejemplo `/identify`

Request:
```json
POST /identify
{
  "templates": [
    { "id": 1234, "template": "AbcDef...==" },
    { "id": 5678, "template": "GhiJkl...==" }
  ]
}
```

Response:
```json
{
  "ok": true,
  "matched": true,
  "id": 1234,
  "score": 87,
  "image": "iVBORw0K..."
}
```

---

## Troubleshooting

### "No se detecto ningun lector ZK conectado por USB"
- Verificar que `ZKFinger Demo` lo ve.
- Reconectar el USB en otro puerto (preferentemente USB 2.0 directo del motherboard, no por hub).

### El SDK detecta 2+ dispositivos pero solo tengo uno conectado
Es comun: instalaciones repetidas del SDK dejan dispositivos virtuales registrados.
Por defecto el agente abre el ultimo indice (count-1), que suele ser el USB fisico.

Si igual no captura huella, forzar otro indice via variable de entorno antes de
ejecutar `run.bat`:

```bat
set ZKFP_DEVICE_INDEX=0
run.bat
```

Probar con `0`, `1`, `2` segun cuantos dispositivos detecte el log
(`Dispositivos detectados: N`).

### El lector se inicializa pero "AcquireFingerprint" siempre devuelve None
Sintomas en el log:
```
capture_one: aun esperando huella (40 polls hechos)
...
capture_one: TIMEOUT tras 294 polls sin detectar huella
```
Causas mas frecuentes:
1. Estamos abriendo un dispositivo fantasma -> probar `ZKFP_DEVICE_INDEX` (ver arriba).
2. `ZKFinger Demo` tampoco lee la huella -> problema de driver, reinstalar SDK.
3. El sensor del lector esta sucio o el dedo apoyado fuera del area sensible.

### `Init()` falla con error -1 / "Failed to initialize the algorithm library"
Esto pasa cuando un proceso anterior (otra instancia del agente, ZKFinger Demo,
un test colgado) abrio el lector y NO llamo a `Terminate()` antes de cerrarse.
El sensor queda en estado "zombie" y todos los siguientes `Init()` fallan, aunque
las DLLs del SDK esten bien instaladas.

**Solucion**:
1. Cerrar TODA instancia del agente (Administrador de tareas -> matar `TaxesAgenteHuella.exe`)
   y cualquier ZKFinger Demo abierto.
2. **Desconectar fisicamente** el ZK9500 del USB.
3. Esperar ~10 segundos.
4. Volver a conectarlo (idealmente en otro puerto USB).
5. Volver a iniciar el agente.

Si despues de eso sigue fallando, reiniciar Windows (resetea el driver USB).

### "ImportError: DLL load failed while importing pyzkfp"
- Falta `libzkfp.dll`. Reinstalar **ZKFinger SDK 5.3**.

### El frontend dice "No se puede conectar al agente"
- Verificar que la consola del agente esta corriendo.
- Probar `http://127.0.0.1:7777/status` en el browser.
- En entornos HTTPS (taxes.com.ar): los navegadores modernos permiten fetch a `localhost` sin warning de mixed content. Si tu browser lo bloquea, abrir `chrome://flags/#block-insecure-private-network-requests` y desactivar.

### Mas de un agente corriendo a la vez
El SDK ZK acepta un solo proceso a la vez tomando el lector. Si abris `ZKFinger Demo` y el agente al mismo tiempo, uno de los dos va a fallar al abrir el dispositivo.
