- Nueva página docs/sapb1_manager.md: sesión, funciones, clasificación de errores (SapNoDisponible frente a rechazo de SAP), búsqueda de facturas existentes y cómo se ubica una guía. - procesar_pdfs.md y procesar_xmls.md usaban los nombres anteriores a la división de pipeline.py (_procesar_pdfs, _ruta_carpeta...) y números de línea que envejecen; ahora apuntan a documentos.py. - inventario.md: árbol de dependencias con los módulos nuevos. - pipeline.md: _procesar_factura, importes_coinciden, hana_disponible y buscar_factura_existente; validar_precios.md, hana_manager.md, main.md y procesar_xmls.md con las notas que faltaban; README sin la cifra de pruebas vieja. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
110 lines
4.1 KiB
Markdown
110 lines
4.1 KiB
Markdown
# main.py — Mapa del flujo
|
||
|
||
## Estructura general
|
||
|
||
```
|
||
main.py
|
||
├── Constantes: HORARIOS (7 bloques que cubren las 24 horas)
|
||
├── obtener_intervalo(dt) → (intervalo, modo) | None
|
||
├── segundos_hasta_proximo_inicio(dt) → int
|
||
├── non_negative_int(value) → int (validador argparse)
|
||
└── main() → None (bucle infinito envuelto en try/except KeyboardInterrupt)
|
||
```
|
||
|
||
## Proceso de `main()`
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[Inicio main] --> B[Parsear args --dias]
|
||
B --> C[Cargar .env → EMAIL, DOWNLOAD_FOLDER, OUTPUT_FOLDER]
|
||
C --> Z[try:]
|
||
Z --> D{Bucle infinito}
|
||
|
||
D --> E[inicio_ciclo = datetime.now]
|
||
E --> F[obtener_intervalo inicio_ciclo]
|
||
|
||
F -- Sí, está en horario --> G[Extraer intervalo, modo]
|
||
G --> H[run_pipeline]:::highlight
|
||
H --> I[Calcular duración real]
|
||
I --> J{Duración > intervalo?}
|
||
J -- Sí --> K[⚠️ Warning]
|
||
J -- No --> L[dormir = intervalo - duración]
|
||
K --> L
|
||
L --> M[💤 Próximo ciclo en dormir seg]
|
||
M --> N[time.sleep dormir]
|
||
N --> E
|
||
|
||
F -- No, fuera de horario --> O[dormir = segundos_hasta_proximo_inicio]
|
||
O --> P[Log próxima ejecución]
|
||
P --> N
|
||
|
||
D -. KeyboardInterrupt .-> Q[🛑 Demonio detenido por el usuario]
|
||
|
||
classDef highlight fill:#fff3e0,stroke:#e65100,stroke-width:3px;
|
||
```
|
||
|
||
### Paso a paso
|
||
|
||
```
|
||
main()
|
||
│
|
||
├── 1. Parsear args (--dias, default=2)
|
||
│
|
||
├── 2. Cargar .env → EMAIL, DOWNLOAD_FOLDER, OUTPUT_FOLDER
|
||
│
|
||
├── 3. try:
|
||
│ └── LOOP INFINITO (while True):
|
||
│ │
|
||
│ ├── 3.1. inicio_ciclo = datetime.now()
|
||
│ │
|
||
│ ├── 3.2. resultado = obtener_intervalo(inicio_ciclo)
|
||
│ │ │
|
||
│ ├── ¿Sí está en horario?
|
||
│ │ │
|
||
│ │ ├── Extraer (intervalo, modo)
|
||
│ │ ├── run_pipeline(dias, EMAIL, DOWNLOAD_FOLDER, OUTPUT_FOLDER)
|
||
│ │ ├── Calcular duración real del ciclo
|
||
│ │ ├── ⚠️ Si duración > intervalo → warning
|
||
│ │ └── dormir = max(0, intervalo - duración)
|
||
│ │
|
||
│ └── ¿No está en horario?
|
||
│ │
|
||
│ ├── dormir = segundos_hasta_proximo_inicio() (06:30 del día siguiente; rama inalcanzable mientras los bloques cubran el día)
|
||
│ └── Log "Fuera del horario, próxima ejecución en..."
|
||
│ │
|
||
│ └── 3.3. time.sleep(dormir) → vuelve a 3.1
|
||
│
|
||
└── except KeyboardInterrupt: 🛑 "Demonio detenido por el usuario" (sin traceback)
|
||
```
|
||
|
||
## Bloques horarios definidos
|
||
|
||
| Desde | Hasta | Intervalo | Modo |
|
||
|---------|---------|-----------|----------|
|
||
| 00:00 | 05:00 | 60s | rápido |
|
||
| 05:00 | 06:30 | 1800s | full |
|
||
| 06:30 | 13:15 | 300s | rápido |
|
||
| 13:15 | 14:00 | 1800s | full |
|
||
| 14:00 | 18:30 | 300s | rápido |
|
||
| 18:30 | 21:30 | 3600s | full |
|
||
| 21:30 | 24:00 | 3600s | full |
|
||
|
||
Los bloques cubren las 24 horas sin huecos ni solapes (lo comprueba `tests/test_horarios.py`). El modo (`rápido`/`full`) solo se registra en el log: `run_pipeline` se ejecuta igual en ambos. Antes de la versión 1.2.0 los tramos 21:30–00:00 y 05:00–06:30 no tenían bloque y el demonio dormía hasta las 06:30.
|
||
|
||
## Entry point
|
||
|
||
```
|
||
if __name__ == "__main__":
|
||
1. logger_manager.setup_logging()
|
||
└── Configura logging a archivo rotado (logs/descarga_factura_transporte.log,
|
||
rotación diaria, 30 días de historial) + consola, y redirige print()/stderr
|
||
al mismo log (los módulos del pipeline ya usan logger con nivel explícito;
|
||
los print() restantes se registran como INFO).
|
||
2. logger.info(...) banner de inicio: fecha, versión del proyecto (pyproject.toml) y logger_manager.LOG_FILE
|
||
3. main()
|
||
└── Tras load_dotenv(), config_validator.verificar_configuracion():
|
||
termina con código 1 si falta alguna variable obligatoria del .env
|
||
(ver config_validator.OBLIGATORIAS) y advierte si la verificación SSL
|
||
está desactivada o no hay ruta de red.
|
||
```
|