Migra el servicio a arquitectura Docker persistente y mueve credenciales HANA a .env
- Reemplaza config_loader/config_model/hana_client/logger_utilities/outlook_utilities por core.py (pipeline funcional), hana_manager.py, logger_manager.py, scheduler.py y descargador_sovos.py (Playwright/Firefox) según la plantilla de servicios. - main.py pasa a loop continuo dirigido por scheduler.json, con manejo de SIGINT/SIGTERM. - Agrega Dockerfile, docker-compose.yml (restart unless-stopped) y certs/ para despliegue. - Agrega smb_transfer.py para la transferencia a carpeta compartida. - Saca las credenciales de HANA de config.json y las mueve a .env (HANA_HOST/PORT/USER/PASSWORD).
This commit is contained in:
1 parent
1503d6a2ec
commit
277d07dfeb
22 files changed
+1081
-750
No files matched your search
@@ -0,0 +1,66 @@
|
||||
# CLAUDE.md — Convenciones del proyecto
|
||||
|
||||
Servicio Python de **descarga de comprobantes electrónicos (CPE) desde Sovos/SUNAT**.
|
||||
Sigue la estructura de la plantilla `~/Desarrollo/Python/plantilla-servicio/`.
|
||||
|
||||
## Estructura
|
||||
|
||||
```
|
||||
main.py # entrada delgada: while loop dirigido por scheduler.json, llama ejecutar()
|
||||
core.py # pipeline funcional: describir_servicio/obtener_datos/procesar_y_descargar/ejecutar
|
||||
scheduler.py # reutilizable (franja activa / próximo inicio a partir de scheduler.json)
|
||||
logger_manager.py # reutilizable (logging niveles + árbol; TimedRotatingFileHandler 30 días)
|
||||
hana_manager.py # reutilizable (HANA multi-schema, obtener_pendientes, marcar)
|
||||
descargador_sovos.py # específico: descarga PDF/XML en Sovos con Playwright (Firefox)
|
||||
smb_transfer.py # reutilizable (detectar/montar SMB + rsync por empresa)
|
||||
config.json # hana, rucs, consulta.dias, empresas(locked), files
|
||||
scheduler.json # HORARIOS ([hora_ini,min_ini,hora_fin,min_fin,modo]) + DIAS_ACTIVOS
|
||||
.env # SMB_* + SMB_AUTO_MOUNT + LOG_LEVEL
|
||||
|
||||
Dockerfile # imagen (Playwright+Firefox ya incluidos, CA Fortinet, rsync)
|
||||
docker-compose.yml # servicio `descarga-cpe` persistente (restart: unless-stopped)
|
||||
certs/fortinet-root.crt # CA de inspección SSL de la red (FG100FTK22018356)
|
||||
```
|
||||
|
||||
## Reglas
|
||||
1. Empresas en `config.json` con `{ nombre, schema, locked, smb_subfolder }`.
|
||||
`locked: true` = no ejecutar. Descargas en `downloads/<nombre>/`.
|
||||
2. Credenciales en `.env`, nunca en git.
|
||||
3. Logging con `logger_manager.get_logger(...)` y niveles info/warning/error (sin `print`).
|
||||
4. `core.py` es funcional (sin clases): cada servicio reescribe `describir_servicio()`,
|
||||
`obtener_datos(empresa)` y `procesar_y_descargar(datos, dir_empresa)`;
|
||||
`main.py` siempre delgado (solo llama a `ejecutar()`; no reescribe lógica de
|
||||
negocio — su única lógica propia es el loop/scheduler de la regla 8).
|
||||
5. Después de descargar PDF/XML se marca en HANA (`U_PGX_PDF`/`U_PGX_XML`) por schema.
|
||||
6. Transferencia con `smb_transfer.transferir_a_smb(...)`; si el SMB no está montado
|
||||
conserva los archivos (no los borra).
|
||||
7. Verificar: `python -m py_compile main.py core.py scheduler.py hana_manager.py descargador_sovos.py smb_transfer.py logger_manager.py`
|
||||
y correr `docker compose up -d descarga-cpe` (revisar con `docker compose logs -f`).
|
||||
8. **Solo Docker, servicio persistente** (no hay venv nativa ni LaunchAgent/cron del
|
||||
HOST en el proyecto): `docker-compose.yml` usa `restart: unless-stopped` y
|
||||
`main.py` mantiene un `while` continuo — en cada ciclo llama a `ejecutar()`
|
||||
(pipeline de `core.py`) y luego duerme según el modo de la franja horaria activa.
|
||||
`scheduler.py` lee `scheduler.json` (releído en cada ciclo, así se pueden editar
|
||||
horarios sin reiniciar el contenedor) y responde:
|
||||
- si el día/hora actual cae dentro de una franja de `HORARIOS`, su `modo`
|
||||
(`"rapido"` → dormir 10 min, `"full"` → dormir 1 hora; mapeo fijo en
|
||||
`INTERVALOS_POR_MODO` dentro de `scheduler.py`, no en el JSON);
|
||||
- si no, el inicio de la próxima franja activa (respetando `DIAS_ACTIVOS`),
|
||||
y `main.py` duerme exactamente hasta ese instante.
|
||||
El sueño se hace en pasos de 1s (`_dormir_interrumpible`) y `main.py` registra
|
||||
handlers de `SIGINT`/`SIGTERM` para poder frenar el loop y salir limpio ante
|
||||
`docker compose stop/down`, en vez de que Docker tenga que matarlo por timeout.
|
||||
Una excepción no controlada en un ciclo se loguea (`exc_info=True`, va a
|
||||
`logs/descarga-cpe.log` con su rotación de 30 días) y el loop reintenta tras
|
||||
60s — ya no hay cron externo que reintente por nosotros.
|
||||
Como el contenedor ya no es efímero, `docker-compose.yml` limita el driver
|
||||
`json-file` de Docker (`max-size: 10m`, `max-file: 5`) para que el log propio
|
||||
del contenedor (stdout) no crezca sin límite; esto es independiente de la
|
||||
rotación de `logs/descarga-cpe.log`, que maneja `logger_manager.py`.
|
||||
9. `scheduler.json` define `HORARIOS` (lista de `[hora_ini, min_ini, hora_fin,
|
||||
min_fin, modo]`, `modo` es `"rapido"` o `"full"`) y `DIAS_ACTIVOS` (0=lunes …
|
||||
6=domingo, según `datetime.weekday()`). Fuera de esas franjas y días no se
|
||||
ejecuta nada.
|
||||
10. Bloqueos de red (FortiGuard Web Filter, p.ej. categoría "Finance and Banking" en
|
||||
`public-einvoicing.sovos.com`) no son bug de la app: se resuelven en el FortiGate,
|
||||
no reintentando ni parcheando el código.
|
||||
Reference in new issue
Block a user